WGCNA on PBMC baseline (original)

First, we run WGCNA on the original DESeq2 normalized data.

Save the following code block as R file to load in and match the clinical data,fpkm matrix, and the sample mapping matrix.

# /home/xy48/Rprogram/GRADS_PBMC/WGCNA/0_CleanDataLoad.R
home.dir<-"/home/yanxiting/driver_Grace"
#home.dir<-"/home/xy48"
data.dir<-file.path(home.dir,"scratch/GRADS/SARC_results/Results_summary_PBMC_hg38")
fpkm.filepath<-file.path(home.dir,"scratch/GRADS/SARC_results/Results_summary_PBMC_hg38/baseline/data","DESeq2_normalized_276_clean.txt")
masterkey.filepath<-file.path(home.dir,"scratch/GRADS/SARC_results/Data/GRADS Master Progress Key 6-13-16.xlsx")

# load in the list of GRADS IDs and KIT IDs from the master key file
mkey<-read.xls(masterkey.filepath,sheet=3,skip=2,header=T,check.names=F,stringsAsFactors = F)
mkey <- mkey[,1:5]

# load in the fpkm matrix
fpkm.matrix<-read.table(fpkm.filepath,sep="\t",header=T,check.names=F,as.is=TRUE,comment.char = "")
fpkm.matrix.anno<-fpkm.matrix[,1:6]
fpkm.matrix<-fpkm.matrix[,7:ncol(fpkm.matrix)]
data.matrix<-fpkm.matrix

# remove the GRADS IDs that do not have the FPKMs
temp.names<-colnames(fpkm.matrix)
matched.list <- mkey[mkey[,1]%in%temp.names,]
matched.list$`Phenotype/Genotype` <- as.factor(matched.list$`Phenotype/Genotype`)

# generate a matrix that match the columns of data.matrix
sample.matrix<-matched.list
rownames(sample.matrix)<-sample.matrix[,1]
sample.matrix<-sample.matrix[colnames(data.matrix),]

Load in clinical matrix and extract data for the variables needed to correlate with the gene modules. Some of the clinical featuers were also re-coded.

#-------------------------------------------------------------------------------------------------# /Rprogram/GRADS_PBMC/WGCNA/6_0_DataLoad.R
###################################################################
#  Load in both the gene expression and the clinical data
#  filter the genes based on given cv.threshold and match the gene expression data and the clinical data
#home.dir<-"/home/xy48"
home.dir<-"/home/yanxiting/driver_Grace"
source(file.path(home.dir,"Rprogram/GRADS_PBMC/WGCNA/0_CleanDataLoad_DESeq2Norm_original.R"))
output.dir<-file.path(home.dir,"scratch/GRADS/SARC_results/Results_summary_PBMC_hg38")
output.dir<-file.path(home.dir,"scratch/GRADS/SARC_results/Results_summary_PBMC_hg38/baseline/WGCNA/WGCNA_DESeq2Norm_original")

if(file.exists(output.dir)==F){
dir.create(output.dir)  
}

#=====================================================================================
#
#  Load in both the gene expression and the clinical data
#  Filter the genes based on CV and match the gene expression and the clinical data
#
#=====================================================================================
#clinic.filepath<-file.path(home.dir,"/home/yanxiting/Research/GRADS_SARC/Data/SARC_combine_clinical_data20170814.txt"
#clinic.filepath<-file.path(home.dir,"scratch/GRADS/SARC_results/Results_summary_PBMC_hg38/data/clin_data_visit1_trunc_XY.csv")
clinic.filepath<-file.path(home.dir,"scratch/GRADS/SARC_results/Results_summary_PBMC_hg38/baseline/data/clinic_matrix_merged.RDS")
#clinic.matrix=read.csv(clinic.filepath,check.names=F,stringsAsFactors=F)
#rownames(clinic.matrix)<-as.matrix(clinic.matrix)[,1] # GRADS ID
clinic.matrix<-readRDS(clinic.filepath,refhook = NULL)
clinic.matrix<-clinic.matrix[sample.matrix[,1],]

# load in the gli equation PFTs
gli.filepath<-file.path(home.dir,"scratch/GRADS/SARC_results/Results_summary_PBMC_hg38/baseline/data/nhanes_gli_adjustedpft.csv")
gli.data<-read.csv(gli.filepath,check.names = F,stringsAsFactors = F)
rownames(gli.data)<-gli.data[,1]
gli.data<-gli.data[rownames(clinic.matrix),]

# merge the gli data into the clinic.matrix
clinic.matrix<-cbind(clinic.matrix,gli.data[,4:ncol(gli.data)])

##########for BAL samples
# only keep genes expressed (fpkm>0.01 in >10% samples) across all samples
bal.fpkm.matrix<-data.matrix
bal.clinic.matrix<-clinic.matrix

bal.fpkm.matrix<-bal.fpkm.matrix[apply(bal.fpkm.matrix>0,1,sum)>ncol(bal.fpkm.matrix)*0.1,]

########## filter genes using SD>0 and genes using CV>cv.threshold################
temp.sd<-apply(bal.fpkm.matrix,1,sd)
bal.fpkm.matrix <- bal.fpkm.matrix[temp.sd>0,]

#temp.mean<-apply(bal.fpkm.matrix,1,mean)
#temp.sd<-apply(bal.fpkm.matrix,1,sd)
#temp.cv<-temp.sd/temp.mean

#my.data.matrix<-bal.fpkm.matrix[temp.cv>=cv.threshold,]
my.data.matrix<-bal.fpkm.matrix

library(WGCNA)
collectGarbage()

datExpr<-t(my.data.matrix)

# remove certain columns in the clinical data
#datTraits<-bal.clinic.matrix[,-c(1,6,8,11:33,35:36,38:41)]
datTraits<-bal.clinic.matrix[,c("AGE",
                                "GENDER",
                                "RACE",
                                "BDFVC",
                                "FVCPRED",
                                "ppfvcpreNhanes",
                                "ppfvcpostNhanes",
                                "ppfvcpreGli",
                                "ppfvcpostGli",
                                "BDFEV1",
                                "FEV1PRED",
                                "ppfevpreNhanes",
                                "ppfevpostNhanes",
                                "ppfevpreGli",
                                "ppfevpostGli",
                                "BDDLCO",
                                "PREDDLCO",
                                "ethor",
                                "num_organ",
                                "wbc",
                                "cd4",
                                "cd8",
                                "cal",
                                "d25",
                                "d125",
                                "CRP",
                                "smoke",
                                "pk_yr",
                                "steroid_atv1",
                                "dmard_atv1",
                                "p_mono",
                                "p_eos",
                                "p_lymph",
                                "p_neut",
                                "p_baso",
                                "SCADDING",
                                "nodule_lymph_pheno",
                                "PHENGRP")]
datTraits$num_organ<-as.numeric(datTraits$num_organ)

# add more variables
#"ethor": binary
#"num_organ": ordinal
#"cd4":
#"d125":

#expecting: 
#smoking
#medication

# change PHENGRP and SCADDING into numbers
temp.vect<-datTraits[,"SCADDING"]
temp.num<-as.numeric(factor(temp.vect,levels=c("0","I","II","III","IV","N/A")))
temp.num[temp.num==6]<-NA
datTraits[,"SCADDING"]<-temp.num

# recode PHENGRP to treatment
temp.vect<-datTraits[,"PHENGRP"] 
temp.num<-rep(0,length(temp.vect)) # untreated all samples
temp.num[temp.vect%in%c("Stage II-III, treated","Stage IV, treated")]<-1
temp.num[temp.vect%in%c("Multi-organ","Cardiac defining therapy")]<-NA

datTraits<-cbind(datTraits,temp.num)
colnames(datTraits)[ncol(datTraits)]<-"TREATMENT"

# add treatment for stage II-III
temp.vect<-datTraits[,"PHENGRP"] 
temp.num<-rep(0,length(temp.vect))
temp.num[!temp.vect%in%c("Stage II-III, untreated","Stage II-III, treated" )]<-NA
temp.num[temp.vect%in%c("Stage II-III, treated")]<-1

datTraits<-cbind(datTraits,temp.num)
colnames(datTraits)[ncol(datTraits)]<-"TREATMENT_STAGEII-III"

# add treatment for stage IV
temp.vect<-datTraits[,"PHENGRP"] 
temp.num<-rep(0,length(temp.vect))
temp.num[!temp.vect%in%c("Stage IV, treated","Stage IV, untreated")]<-NA
temp.num[temp.vect%in%c("Stage IV, treated")]<-1

datTraits<-cbind(datTraits,temp.num)
colnames(datTraits)[ncol(datTraits)]<-"TREATMENT_STAGEIV"

datTraits<-datTraits[,colnames(datTraits)!="PHENGRP"]

# change RACE into a binary variable
temp.vect<-datTraits[,"RACE"]
temp.vect[!temp.vect%in%c(0,1)]<-NA
datTraits[,"RACE"]<-temp.vect

# recode the nodule lymphdenopathy phenotype
temp.vect<-datTraits[,"nodule_lymph_pheno"]
temp.num<-as.numeric(factor(temp.vect,levels=c("none","lymph","micronodule","both")))
datTraits[,"nodule_lymph_pheno"]<-temp.num

# add more group comparisons
temp.names<-rownames(datTraits)
datTraits<-t(datTraits)

# change the names of the cell differentials
#rownames(datTraits)[rownames(datTraits)=="p_lymph"]<-"LYMBALD"
#rownames(datTraits)[rownames(datTraits)=="p_mono"]<-"MONOBALD"
#rownames(datTraits)[rownames(datTraits)=="p_neut"]<-"NEUBALD"
#rownames(datTraits)[rownames(datTraits)=="p_eos"]<-"EOSBALD"
#rownames(datTraits)[rownames(datTraits)=="p_baso"]<-"BASBALD"



# check for the sum of cell differentials

datTraits<-datTraits[c(
"AGE",
"GENDER",
"RACE",
"BDFVC",
"FVCPRED",
"ppfvcpreNhanes",
"ppfvcpostNhanes",
"ppfvcpreGli",
"ppfvcpostGli",
"BDFEV1",
"FEV1PRED",
"ppfevpreNhanes",
"ppfevpostNhanes",
"ppfevpreGli",
"ppfevpostGli",
"BDDLCO",
"PREDDLCO", # add the gli
"ethor",
"num_organ",
"wbc",
"cd4",
#"cd8", # probably not important
"cal",
"d25",
"d125",
"CRP",
"smoke",
"pk_yr",
"steroid_atv1",
"dmard_atv1",
"p_mono",
"p_eos",
"p_lymph",
"p_neut",
"p_baso",
"SCADDING",
"nodule_lymph_pheno"
),]

# set the negative values in the trait matrix into NAs
datTraits["p_lymph",datTraits["p_lymph",]==225.3]<-NA
datTraits[datTraits<0]<-NA

# set the cell differentials with 0 macrophages to be NAs
#datTraits[,(!is.na(datTraits["ALVBALD",])) & #datTraits["ALVBALD",]==0][c("ALVBALD","EOSBALD","LYMBALD","NEUBALD"),]<-NA
save.image(file.path(output.dir,"input_data.RData"))

Generate the hierarhical clustering tree using the genes to identify potential outliers.

#----------------------------------------------------------------------------------------------------------------------------------------------------
# /Rprogram/GRADS_PBMC/WGCNA/6_1_sampletree.R
rdata.filepath<-file.path(output.dir,"input_data.RData")
library(WGCNA)

load(rdata.filepath)
sampleTree = hclust(dist(datExpr), method = "average");

# save the hierarchical tree before removing the outliers
output.filepath<-file.path(output.dir,"hclust_tree_allsamples.eps")
#sizeGrWindow(12,9)
#pdf(file = output.filepath);
postscript(output.filepath,width=10,height=10,paper="special")
par(cex = 0.6);
par(mar = c(0,4,2,0))
plot(sampleTree, main = "Sample clustering to detect outliers", sub="", xlab="", cex.lab = 1.5,
cex.axis = 1.5, cex.main = 2,cex=0.55)
#save plot
dev.off()

# output the heights on the tree so we can decide which heights to cut
output.filepath<-file.path(output.dir,"sampletree_height_list.txt")
cat(sampleTree$height,sep="\n",file=output.filepath,append=F)

We need to go over the sampletree_height_list.txt and the hcluste_tree_allsamples.eps to decide where to cut on the hierarchical clustering tree. The first number in the files should be a number larger than the largest number in sampletree_height_list.txt to make sure we also run the program without removing any samples. Then each number in the file should be slightly smaller than the actual number in sampletree_height_list.txt to make sure we cut underneath the branch.

We need to generate the hclust_cut_heights_list.txt to provide the heights at which we would like to cut the hierarchical clustering tree. Name of this file cannot be changed and it has to be saved under the same folder where all WGCNA results are saved.

#----------------------------------------------------------------------------------------------------------------------------------------------------
# /Rprogram/GRADS/BAL_final_hg38_20180726/WGCNA_unadjusted_ALL_filtering/6_2_sftpicking.R
####################################################
# load in all cut heights for a given cv.threshold, generate the soft power picking figures for each cut height
rdata.filepath<-file.path(output.dir,"input_data.RData")
cutheight.filepath<-file.path(output.dir,"hclust_cut_heights_list.txt")
library(WGCNA)
load(rdata.filepath)
enableWGCNAThreads(nThreads = 15)
# load in the height vector
#height.vect<-c(50000,48000,38080,37970,33640,25100,23700,19260,18260)
height.vect<-scan(cutheight.filepath)

sampleTree = hclust(dist(datExpr), method = "average")

outlier.list<-list()
outlier.list[[1]]<-character()
for(i in 1:length(height.vect)){
clust = cutreeStatic(sampleTree, cutHeight = height.vect[i], minSize = 10)
keepSamples = (clust==1)
outlier.list[[i]]<-setdiff(rownames(datExpr)[!keepSamples],unlist(outlier.list))
}

# output the height of cut, the number of outliers and the IDs of the outliers into one file
cmd.out<-cbind(height.vect,unlist(lapply(outlier.list,length)),unlist(lapply(outlier.list,paste,collapse=";")))
colnames(cmd.out)<-c("cut_height","outlier_number","outliers_ids")
output.filepath<-file.path(output.dir,"cutheight_2_sft.txt")
write.table(cmd.out,sep="\t",file=output.filepath,append=F,quote=F,row.names=F,col.names=T)


####################################################
# 6.1 for each given cut height, generate the soft power picking figures to pick soft power for each given cut height 
# save the soft power picking figure into the same file for reviewing
pdf.filepath<-file.path(output.dir,"sft_picking.pdf")
pdf(pdf.filepath)
for(i in 1:length(outlier.list)){
#clust = cutreeStatic(sampleTree, cutHeight = height.vect[i], minSize = 10)
#keepSamples = (clust==1)
my.datExpr = datExpr[setdiff(rownames(datExpr),unlist(outlier.list[1:i])), ]


#rename the columns (remove all after _ in sample name)
SarcaleSamples = rownames(my.datExpr)
sampleRows =match(SarcaleSamples,rownames(sample.matrix));
sample.matrix.new=sample.matrix[sampleRows,]
traitRows = match(sample.matrix.new[,1],colnames(datTraits));
my.datTraits = datTraits[,traitRows];

nGenes = ncol(my.datExpr)
nSamples = nrow(my.datExpr)

powers = c(c(1:10), seq(from = 12, to=20, by=2))
# Call the network topology analysis function
sft = pickSoftThreshold(my.datExpr, powerVector = powers, verbose = 5)


# save the plot for picking the soft power
#output.filepath<-file.path(output.dir,"SoftPowerPick.eps")
#postscript(output.filepath,width=9,height=5,paper="special")
#sizeGrWindow(9, 5)
par(mfrow = c(1,2));
cex1 = 0.9;
# Scale-free topology fit index as a function of the soft-thresholding power
plot(sft$fitIndices[,1], -sign(sft$fitIndices[,3])*sft$fitIndices[,2],
xlab="Soft Threshold (power)",
ylab="Scale Free Topology Model Fit,signed R^2",type="n",
main = paste("Scale independence"));
text(sft$fitIndices[,1], -sign(sft$fitIndices[,3])*sft$fitIndices[,2],
labels=powers,cex=cex1,col="red");
# this line corresponds to using an R^2 cut-off of h
abline(h=0.90,col="red")
# Mean connectivity as a function of the soft-thresholding power
plot(sft$fitIndices[,1], sft$fitIndices[,5],
xlab="Soft Threshold (power)",ylab="Mean Connectivity", type="n",
main = paste("Mean connectivity\n","outlier.list[",i,"]",sep=""))
text(sft$fitIndices[,1], sft$fitIndices[,5], labels=powers,
cex=cex1,col="red")
#output.filepath<-file.path(output.dir,"SoftPowerPick.jpg")
#savePlot(output.filepath,type="jpeg")
#dev.off()

}
dev.off()

save.image(file.path(output.dir,"sft_picking.RData"))

We need to review the plots in sft_picking.pdf and generate a text file with each line describing the softpower we want to set for each cut height we picked before. This text file will be saved as sft_power_list.txt. The numbers will be in the same order as those plots in the sft_picking.pdf.

#---------------------------------------------------------------------------------------------------------------------------------------------------
# /Users/yanxiting/MyVolumes/GRACE/Rprogram/GRADS/BAL_final_hg38_20180726/WGCNA_unadjusted_ALL_filtering/6_3_clustering.R
#output.dir<-file.path(home.dir,"scratch/GRADS/SARC_results/Results_summary_PBMC_hg38/CV0_compare_V2")
rdata.filepath<-file.path(output.dir,"sft_picking.RData")
sftpower.filepath<-file.path(output.dir,"sft_power_list.txt")
load(rdata.filepath)

library(WGCNA)
enableWGCNAThreads(nThreads = 15)
library(gplots)


my.cor.dist<-function(x,method.name="spearman"){
# calculate the correlation between rows in x and return the correlation matrix as a distance matrix
y<-cor(t(x),method=method.name)
y[is.na(y)]<- -1.5
return(as.dist(1-y))
}


# for each element in outlier.list, pick power and put them in power.vect below for further analysis
#power.vect<-c(3,4,5,3,4,3,3,3,4)
power.vect<-scan(sftpower.filepath)

net.list<-list()
# obtain the WGCNA results for each given set of outliers
for(i in 1:length(outlier.list)){
gc()
cat("i=",i,"\n",sep="")
#clust = cutreeStatic(sampleTree, cutHeight = height.vect[i], minSize = 10)
#keepSamples = (clust==1)
my.datExpr = datExpr[setdiff(rownames(datExpr),unlist(outlier.list[1:i])), ]

#rename the columns (remove all after _ in sample name)
SarcaleSamples = rownames(my.datExpr)
sampleRows =match(SarcaleSamples,rownames(sample.matrix));
sample.matrix.new=sample.matrix[sampleRows,]
traitRows = match(sample.matrix.new[,1],colnames(datTraits));
my.datTraits = datTraits[,traitRows];

nGenes = ncol(my.datExpr)
nSamples = nrow(my.datExpr)

# select the power based on the plot above
power.selected<-power.vect[i]
net = blockwiseModules(my.datExpr, power = power.selected, minModuleSize = 10, maxBlockSize = 30000,
reassignThreshold = 0, mergeCutHeight = 0.25,
numericLabels = TRUE, pamRespectsDendro = FALSE,
saveTOMs = FALSE,
#saveTOMFileBase = paste("CV",cv.threshold,"outlier",i,sep=""),
verbose = 3,nThreads=15)
net.list[[i]]<-net


#------------------------------------
# plot the distance matrix by WGCNA
dissTOM = 1-TOMsimilarityFromExpr(my.datExpr, power = power.selected)
plotTOM = dissTOM^7
diag(plotTOM) = NA

#output.filepath<-file.path(output.dir,paste("heatmap_module_outliers_",i,".eps",sep=""))
#postscript(output.filepath,width=15,height=15,paper="special")
#sizeGrWindow(15,15)
#temp.error<-try(TOMplot(plotTOM[net$goodGenes,net$goodGenes], net$dendrograms[[1]], labels2colors(net$colors)[net$goodGenes],
#main = paste("Network heatmap plot",sep="")))

#output.filepath<-file.path(output.dir,paste("heatmap_modules_CV_no_log2_without_outlier",cv.threshold,".jpg",sep=""))
#savePlot(output.filepath,type="jpeg")
#dev.off()


#--------------------------------------
# plot the dendrogram
output.filepath<-file.path(output.dir,paste("dendrogram_modules_outliers_",i,".eps",sep=""))
postscript(output.filepath,width=20,height=12,paper="special")
#sizeGrWindow(12, 9)
# Convert labels to colors for plotting
mergedColors = labels2colors(net$colors)
# Plot the dendrogram and the module colors underneath
plotDendroAndColors(net$dendrograms[[1]], mergedColors[net$blockGenes[[1]]],
"Module\n colors",
rowText = mergedColors[net$blockGenes[[1]]],
dendroLabels = FALSE, hang = 0.03,
addGuide = TRUE, guideHang = 0.05,
cex.main = 2, cex.axis = 1.4, cex.lab = 1.4,
cex.colorLabels = 1.2,
marAll= c(0, 5, 3, 3))
#savePlot(output.filepath,type="jpeg")
dev.off()

#----------------------------------------
# calculate the correltion between the eigen genes of the modules to see if we need to change the mergeCutHeight
MEList.test<-moduleEigengenes(my.datExpr,colors=mergedColors)
MEs.test<-MEList.test$eigengenes
MEDiss.test<-1-cor(MEs.test)
METree.test=hclust(as.dist(MEDiss.test),method="average")

#sizeGrWindow(7,6)
output.filepath<-file.path(output.dir,paste("hclust_modules_outliers_",i,".eps",sep=""))
postscript(output.filepath,width=20,height=12,paper="special")
plot(METree.test,main="Clustering of module eigengenes",xlab="",sub="")
abline(h=0.25,col="red")
dev.off()

output.filepath<-file.path(output.dir,paste("correlation_modules_outliers_",i,".txt",sep=""))
cat("\t",file=output.filepath,apend=F)
write.table(1-MEDiss.test,file=output.filepath,append=T,sep="\t",row.names=T,col.names=T,quote=F)

#-----------------------------------------------
# plot the correlation between the modules with the clinical traits
#datExpr<-data.matrix[clustering.result==i,ps.names]
#datTraits<-as.data.frame(t(my.clinic.data[,clustering.result==i]))
nGenes = ncol(my.datExpr);
nSamples = nrow(my.datExpr);
# Recalculate MEs with color labels
MEs0 = moduleEigengenes(my.datExpr, labels2colors(net$colors))$eigengenes
MEs = orderMEs(MEs0)
cp = bicorAndPvalue(MEs, t(my.datTraits))
moduleTraitCor = cp$bicor;
moduleTraitPvalue = cp$p;

output.filepath<-file.path(output.dir,paste("heatmap_trait_correlation_modules_outliers_",i,".eps",sep=""))
#pdf(file = output.filepath,width = 15,height = 9,paper="special")
postscript(output.filepath,width=15,height=24,paper="special")
#sizeGrWindow(15,9)
#pdf(file="Plots/moduleTraitRelationships.pdf", width=10, height=6);
# Will display correlations and their p-values
textMatrix = paste(signif(moduleTraitCor, 2), "\n(",signif(moduleTraitPvalue, 1), ")", sep = "");
dim(textMatrix) = dim(moduleTraitCor)
par(mar = c(12, 12, 3, 1));
# Display the correlation values within a heatmap plot
labeledHeatmap(Matrix = moduleTraitCor,
xLabels = rownames(my.datTraits[]),
yLabels = names(MEs),
ySymbols = names(MEs),
colorLabels = FALSE,
colors = greenWhiteRed(50),
textMatrix = textMatrix,
setStdMargins = FALSE,
cex.text = 0.5,
zlim = c(-1,1),
main = paste("Module-trait relationships"))
#savePlot(output.filepath,type="jpeg")
dev.off()




output.filepath<-file.path(output.dir,paste("heatmap_trait_correlation_modules_outliers_",i,"_pvaluesig.eps",sep=""))
#pdf(file = output.filepath,width = 15,height = 9,paper="special")
#sizeGrWindow(15,9)
postscript(output.filepath,width=10,height=10,paper="special")
#pdf(file="Plots/moduleTraitRelationships.pdf", width=10, height=6);
# Will display correlations and their p-values
textMatrix = paste(signif(moduleTraitCor, 2), "\n(",signif(moduleTraitPvalue, 1), ")", sep = "");
dim(textMatrix) = dim(moduleTraitCor)
par(mar = c(12, 12, 3, 1));
# Display the correlation values within a heatmap plot
temp.moduleTraitPvalue<-moduleTraitPvalue
temp.moduleTraitCor<-moduleTraitCor
temp.moduleTraitCor[temp.moduleTraitPvalue>0.05]<-NA

labeledHeatmap(Matrix = temp.moduleTraitCor,
xLabels = rownames(my.datTraits[]),
yLabels = names(MEs),
ySymbols = names(MEs),
colorLabels = FALSE,
colors = blueWhiteRed(50),
textMatrix = textMatrix,
#cex.text=0.8,
setStdMargins = FALSE,
cex.text = 0.5,
zlim = c(-1,1),
main = paste("Module-trait relationships"),
naColor="white")
#savePlot(output.filepath,type="jpeg")
dev.off()

#----------------------------------------
# output the module membership for all genes
output.filepath<-file.path(output.dir,paste("module_assignment_outliers_",i,".txt",sep=""))
ps.names<-rownames(my.data.matrix)
gene.names<-unname(sapply(ps.names,my.element.remove,splitchar="_",index=-1))
cmd.out<-cbind(ps.names,gene.names,labels2colors(net$colors))
write.table(cmd.out,file=output.filepath,append=F,sep="\t",row.names=F,col.names=F,quote=F)

#--------------------------------
# output the module membership and the gene significance for each module
nGenes = ncol(my.datExpr);
nSamples = nrow(my.datExpr);
# Recalculate MEs with color labels
MEs0 = moduleEigengenes(my.datExpr, labels2colors(net$colors))$eigengenes
MEs = orderMEs(MEs0)

geneModuleMembership = as.data.frame(cor(my.datExpr, MEs, use = "p"));
MMPvalue = as.data.frame(corPvalueStudent(as.matrix(geneModuleMembership), nSamples));

geneTraitSignificance = as.data.frame(cor(my.datExpr, t(my.datTraits), use = "p"));
GSPvalue = as.data.frame(corPvalueStudent(as.matrix(geneTraitSignificance), nSamples));

# output the gene module membership together with their p values
modNames=substring(names(MEs), 3)
output.subdir<-file.path(output.dir,paste("ModuleMembership_GSPvalue_outliers_",i,sep=""))
if(file.exists(output.subdir)==F){
dir.create(output.subdir)   
}
for(k in 1:length(modNames)){
output.filepath<-file.path(output.subdir,paste("Module_",modNames[k],"_membership_gsig.txt",sep=""))
ps.names<-rownames(my.data.matrix)
gene.names<-unname(sapply(ps.names,my.element.remove,splitchar="_",index=-1))
temp.color.desig<-labels2colors(net$colors)
cmd.out<-cbind(ps.names[temp.color.desig==modNames[k]],gene.names[temp.color.desig==modNames[k]],geneModuleMembership[temp.color.desig==modNames[k],colnames(geneModuleMembership)==paste("ME",modNames[k],sep="")],MMPvalue[temp.color.desig==modNames[k],colnames(MMPvalue)==paste("ME",modNames[k],sep="")],geneTraitSignificance[temp.color.desig==modNames[k],],GSPvalue[temp.color.desig==modNames[k],])
colnames(cmd.out)<-c("ps_names","gene_names","module_membership","module_membership_pvalue",paste("gs_",rownames(my.datTraits),sep=""),paste("gs_",rownames(my.datTraits),"_pvalue",sep=""))
write.table(cmd.out,file=output.filepath,append=F,sep="\t",row.names=F,col.names=T,quote=F)
}

#-----------------------------------------------------------
# output the data matrix and the heatmap for the gene expression levels in each module and hierarchical clusteirng of the samples using genes from each module separately

modNames=substring(names(MEs), 3)
output.subdir<-file.path(output.dir,paste("ModuleClustering_outliers_",i,sep=""))
if(file.exists(output.subdir)==F){
dir.create(output.subdir)
}

for(k in 1:length(modNames)){
output.filepath<-file.path(output.subdir,paste("Module_",modNames[k],"_nooutliers_fpkm.txt",sep=""))
ps.names<-rownames(my.data.matrix)
gene.names<-unname(sapply(ps.names,my.element.remove,splitchar="_",index=-1))
temp.color.desig<-labels2colors(net$colors)
cmd.out<-my.data.matrix[temp.color.desig==modNames[k],rownames(my.datExpr)]
#cmd.out<-cmd.out[hclust(my.cor.dist(cmd.out))$order,]
cat("tracking_id\t",file=output.filepath,append=F)
write.table(cmd.out,file=output.filepath,append=T,sep="\t",row.names=T,col.names=T,quote=F)

#temp.breaks<-c(0.01,seq(from=0.01,to=10,length=499),10.1)
output.filepath<-file.path(output.subdir,paste("Heatmap_Module_",modNames[k],"_nooutliers_fpkm_original.eps",sep=""))
postscript(output.filepath)
temp.breaks<-seq(from=as.numeric(quantile(as.matrix(cmd.out),prob=0.05)),to=as.numeric(quantile(as.matrix(cmd.out),prob=0.95)),length=501)
heatmap.2(as.matrix(cmd.out),distfun=my.cor.dist,hclustfun=function(d){return(hclust(d,method="complete"))},breaks=temp.breaks,scale="none",Rowv=TRUE,Colv=TRUE,col=colorpanel(500,low="black",high="red"),cexCol=1,trace="none",density.info="none",key=TRUE,symkey=FALSE,keysize=0.8,labRow="",na.color="grey",main=paste("Module ",modNames[k],sep=""))
dev.off()

output.filepath<-file.path(output.subdir,paste("Heatmap_Module_",modNames[k],"_nooutliers_fpkm_centered.eps",sep=""))
postscript(output.filepath)
cmd.out<-my.normalize(cmd.out)
temp.breaks<-seq(from=as.numeric(quantile(as.matrix(cmd.out),prob=0.05)),to=as.numeric(quantile(as.matrix(cmd.out),prob=0.95)),length=501)
#temp.heatmap<-heatmap.2(as.matrix(cmd.out),distfun=my.cor.dist,breaks=temp.breaks,scale="none",Rowv=TRUE,Colv=TRUE,col=colorpanel(500,low="purple",high="yellow",mid="grey"),cexCol=1,trace="none",density.info="none",key=TRUE,symkey=FALSE,keysize=0.8,labRow="",na.color="grey",main=paste("Module ",modNames[k],sep=""))
temp.heatmap<-heatmap.2(as.matrix(cmd.out),distfun=my.cor.dist,hclustfun=function(d){return(hclust(d,method="complete"))},breaks=temp.breaks,scale="none",Rowv=TRUE,Colv=TRUE,col=colorpanel(500,low="purple",high="yellow",mid="grey"),cexCol=1,trace="none",density.info="none",key=TRUE,symkey=FALSE,keysize=0.8,labRow="",na.color="grey",main=paste("Module ",modNames[k],sep=""))
dev.off()

output.filepath<-file.path(output.subdir,paste("HclustTree_Module_",modNames[k],"_nooutliers_fpkm_centered.eps",sep=""))
postscript(output.filepath)
d <- my.cor.dist(as.matrix(t(cmd.out))) 
# apply hirarchical clustering 
hc <- hclust(d,method="complete")     
plot(hc,main=paste("Module ",modNames[k],sep=""),cex=0.3) 
dev.off()


output.filepath<-file.path(output.subdir,paste("HclustTreeHeatmap2_Module_",modNames[k],"_nooutliers_fpkm_centered.eps",sep=""))
postscript(output.filepath,width=40,height=20)
plot(temp.heatmap$colDendrogram,main=paste("Module ",modNames[k],sep=""),cex=0.3) 
dev.off()

}


modNames=substring(names(MEs), 3)
output.subdir<-file.path(output.dir,paste("ModuleClustering_outliers_",i,sep=""))
if(file.exists(output.subdir)==F){
dir.create(output.subdir)
}

for(k in 1:length(modNames)){
output.filepath<-file.path(output.subdir,paste("Module_",modNames[k],"_ALL_fpkm.txt",sep=""))
ps.names<-rownames(my.data.matrix)
gene.names<-unname(sapply(ps.names,my.element.remove,splitchar="_",index=-1))
temp.color.desig<-labels2colors(net$colors)
cmd.out<-my.data.matrix[temp.color.desig==modNames[k],]
#cmd.out<-cmd.out[hclust(my.cor.dist(cmd.out))$order,]
cat("tracking_id\t",file=output.filepath,append=F)
write.table(cmd.out,file=output.filepath,append=T,sep="\t",row.names=T,col.names=T,quote=F)

#temp.breaks<-c(0.01,seq(from=0.01,to=10,length=499),10.1)
output.filepath<-file.path(output.subdir,paste("Heatmap_Module_",modNames[k],"_ALL_fpkm_original.eps",sep=""))
postscript(output.filepath)
temp.breaks<-seq(from=as.numeric(quantile(as.matrix(cmd.out),prob=0.05)),to=as.numeric(quantile(as.matrix(cmd.out),prob=0.95)),length=501)
heatmap.2(as.matrix(cmd.out),distfun=my.cor.dist,hclustfun=function(d){return(hclust(d,method="complete"))},breaks=temp.breaks,scale="none",Rowv=TRUE,Colv=TRUE,col=colorpanel(500,low="black",high="red"),cexCol=1,trace="none",density.info="none",key=TRUE,symkey=FALSE,keysize=0.8,labRow="",na.color="grey",main=paste("Module ",modNames[k],sep=""))
#heatmap.2(cmd.out,scale="row",Rowv=TRUE,Colv=FALSE,col=colorpanel(500,low="blue",mid="white",high="red"),cexCol=1,trace="none",density.info="none",keysize=0.8,margins=c(7,5.5))
dev.off()

output.filepath<-file.path(output.subdir,paste("Heatmap_Module_",modNames[k],"_ALL_fpkm_centered.eps",sep=""))
postscript(output.filepath)
cmd.out<-my.normalize(cmd.out)
temp.breaks<-seq(from=as.numeric(quantile(as.matrix(cmd.out),prob=0.05)),to=as.numeric(quantile(as.matrix(cmd.out),prob=0.95)),length=501)
temp.heatmap<-heatmap.2(as.matrix(cmd.out),distfun=my.cor.dist,hclustfun=function(d){return(hclust(d,method="complete"))},breaks=temp.breaks,scale="none",Rowv=TRUE,Colv=TRUE,col=colorpanel(500,low="purple",high="yellow",mid="grey"),cexCol=1,trace="none",density.info="none",key=TRUE,symkey=FALSE,keysize=0.8,labRow="",na.color="grey",main=paste("Module ",modNames[k],sep=""))
#heatmap.2(cmd.out,scale="row",Rowv=TRUE,Colv=FALSE,col=colorpanel(500,low="blue",mid="white",high="red"),cexCol=1,trace="none",density.info="none",keysize=0.8,margins=c(7,5.5))
dev.off()


output.filepath<-file.path(output.subdir,paste("HclustTree_Module_",modNames[k],"_ALL_fpkm_centered.eps",sep=""))
postscript(output.filepath)
d <- my.cor.dist(as.matrix(t(cmd.out))) 
# apply hirarchical clustering 
hc <- hclust(d,method="complete")     
plot(hc,main=paste("Module ",modNames[k],sep=""),cex=0.3) 
dev.off()


output.filepath<-file.path(output.subdir,paste("HclustTreeHeatmap2_Module_",modNames[k],"_ALL_fpkm_centered.eps",sep=""))
postscript(output.filepath,width=40,height=20)
plot(temp.heatmap$colDendrogram,main=paste("Module ",modNames[k],sep=""),cex=0.3) 
dev.off()

}



#--------------------------------------------
# output a table to show the number of genes for each module
output.filepath<-file.path(output.dir,paste("genenum_per_module_outliers_",i,".txt",sep=""))
ps.names<-rownames(my.data.matrix)
gene.names<-unname(sapply(ps.names,my.element.remove,splitchar="_",index=-1))
temp.color.desig<-labels2colors(net$colors)
cmd.out<-as.matrix(table(temp.color.desig))
cat("module_names\tgene_num\n",file=output.filepath,append=F)
write.table(cmd.out,file=output.filepath,append=T,sep="\t",col.names=F,row.names=T,quote=F)

}












# visualize and measure the similarity between different clustering results
module.labels.list<-list()
module.colors.list<-list()

module.labels.list[[1]]<-net.list[[1]]$colors
module.colors.list[[1]]<-labels2colors(module.labels.list[[1]])

for(i in 2:length(net.list)){
module.labels.list[[i]]<-matchLabels(net.list[[i]]$colors,module.labels.list[[1]])
module.colors.list[[i]]<-labels2colors(module.labels.list[[i]])
}

# remove genes that were considered as bad genes by net.list[[1]]
my.module.colors.list<-list()
for(i in 1:length(net.list)){
my.module.colors.list[[i]]<-module.colors.list[[i]][net.list[[1]]$goodGenes]
}

geneTree = net.list[[1]]$dendrograms[[1]]
output.filepath<-file.path(output.dir,"dendrogram_outliers_compare.eps")
postscript(output.filepath,width=12,height=24,paper="special")
#postscript(output.filepath)
plotDendroAndColors(geneTree,
                matrix(unlist(my.module.colors.list),nrow=length(my.module.colors.list[[1]]),byrow=F),
                cumsum(unlist(lapply(outlier.list,length))),
                  main = "comparing gene dendrogram",
                  dendroLabels = FALSE, hang = 0.03,
                  addGuide = TRUE, guideHang = 0.05)
dev.off()

save.image(file.path(output.dir,"wgcna_cutheights.RData"))

Here’s the code block that generate the script files to run these jobs on HPC in parallel.

#---------------------------------------------------------------------------------------------------------------------------------------------------
# /Users/yanxiting/MyVolumes/GRACE/Rprogram/GRADS/BAL_final_hg38_20180726/WGCNA_unadjusted_ALL_filtering/6_WGCNA_new.R
####################
# 6.3  for the set of potential outlier, evaluate the effect of these outliers on the WGCNA results

# generate the file to show the CV quantiles.
#dataload.filepath<-"/home/fas/kaminski/xy48/Rprogram/GRADS/BAL_final_20170724/WGCNA_PHGRPfiltering/6_0_DataLoad.R"
#dataload.filepath<-"/home/fas/kaminski/xy48/Rprogram/GRADS/BAL_final_20170724/WGCNA_unadjusted_ALL_filtering/6_0_DataLoad.R"
dataload.filepath<-"/home/yanxiting/Research/GRADS_SARC/Rprogram/WGCNA_unadjusted_ALL_filtering/6_0_DataLoad.R"
output.dir<-"/home/yanxiting/Research/GRADS_SARC/Results_summary_BAL_hg38/WGCNA_BAL/unadjusted_ALL_filtering"

if(file.exists(output.dir)==F){
dir.create(output.dir)  
}

cv.threshold<-0

source("/home/yanxiting/Research/GRADS_SARC/Rprogram/WGCNA_unadjusted_ALL_filtering/0_CleanDataLoad.R")
output.dir<-file.path(output.dir,paste("CV",cv.threshold,"_compare",sep=""))

if(file.exists(output.dir)==F){
dir.create(output.dir)  
}

#=====================================================================================
#
#  Load in both the gene expression and the clinical data
#  Filter the genes based on CV and match the gene expression and the clinical data
#
#=====================================================================================

#clinic.filepath<-"/home/fas/kaminski/xy48/scratch/GRADS/SARC_results/Data/SARC_combine_clinical_data20170814.txt"
clinic.filepath<-"/home/yanxiting/Research/GRADS_SARC/Data/SARC_combine_clinical_data20170814.txt"
clinic.matrix=read.table(clinic.filepath,header=T,sep="\t",check.names=F,stringsAsFactors=F,quote = "")
rownames(clinic.matrix)<-as.matrix(clinic.matrix)[,1] # GRADS ID
clinic.matrix<-clinic.matrix[sample.matrix[,1],]

##########for BAL samples
# only keep genes expressed (fpkm>0.01 in >10% samples) across all samples
bal.fpkm.matrix<-data.matrix[,substr(colnames(data.matrix),3,3)=="B"]
bal.clinic.matrix<-clinic.matrix[substr(colnames(data.matrix),3,3)=="B",]

bal.fpkm.matrix<-bal.fpkm.matrix[apply(bal.fpkm.matrix>0.01,1,sum)>ncol(bal.fpkm.matrix)*0.1,] # 30130 genes reduced to 20828 genes

########## filter genes using SD>0 and genes using CV>cv.threshold################

temp.sd<-apply(bal.fpkm.matrix,1,sd)
bal.fpkm.matrix <- bal.fpkm.matrix[temp.sd>0,]

temp.mean<-apply(bal.fpkm.matrix,1,mean)
temp.sd<-apply(bal.fpkm.matrix,1,sd)
temp.cv<-temp.sd/temp.mean

my.data.matrix<-bal.fpkm.matrix[temp.cv>=cv.threshold,]


# output the proportion, the number of genes remain and the quantiles of the CV into one file
cat("percentile\t# of genes\tCV_quantile\n",file=file.path(output.dir,"/../bal_cv_quantiles.txt"),append=F)
write.table(cbind(seq(from=0,to=1,by=0.01),length(temp.cv)*(1-seq(from=0,to=1,by=0.01)),quantile(temp.cv,prob=seq(from=0,to=1,by=0.01))),file=file.path(output.dir,"/../bal_cv_quantiles.txt"),sep="\t",append=T,quote=F,row.names=F,col.names=F)














##########################
# 6.3.0 load the data into R and save the workspace for further analysis
#dataload.filepath<-"/home/fas/kaminski/xy48/Rprogram/GRADS/BAL_final_20170724/WGCNA_unadjusted_ALL_filtering/6_0_DataLoad.R"
dataload.filepath<-"/home/yanxiting/Research/GRADS_SARC/Rprogram/WGCNA_unadjusted_ALL_filtering/6_0_DataLoad.R"
# choose the following CV threshold using the bal_quantile.txt generated above
#cv.threshold.vect<-c(0,0.2360,0.2888,0.3483,0.4384,0.5717,0.7019,0.8537,1.0571,1.4615)
cv.threshold.vect<-0
#output.dir<-"/home/fas/kaminski/xy48/scratch/GRADS/SARC_results/Results_summary/WGCNA_BAL/unadjusted_ALL_filtering"
output.dir<-"/home/yanxiting/Research/GRADS_SARC/Results_summary_BAL_hg38/WGCNA_BAL/unadjusted_ALL_filtering"

script.dir<-file.path(output.dir,"scripts_dataload_outliercompare")
if(file.exists(script.dir)==F){
dir.create(script.dir)
}

jobsub.filepath<-file.path(script.dir,"jobsub.bat")
if(file.exists(jobsub.filepath)){
file.remove(jobsub.filepath)
}
file.create(jobsub.filepath)

for(i in 1:length(cv.threshold.vect)){
    
cv.threshold<-cv.threshold.vect[i]
script.filepath<-file.path(script.dir,paste("cv",cv.threshold,".sh",sep=""))
#cmd.out<-paste("#BSUB -q kaminski -n 1 -M 8000 -R \"span[ptile=1] rusage[mem=8000]\" -J ",paste("cv",cv.threshold,sep="")," -o ",script.filepath,".o%J -e ",script.filepath,".e%J\n",sep="")
#cmd.out<-paste("#!/bin/bash","\n","#SBATCH --partition=pi_kaminski","\n","#SBATCH --job-name=",paste("cv",cv.threshold,sep=""),"\n","#SBATCH --ntasks=1 --nodes=1 --cpus-per-task=1","\n","#SBATCH --mem=16000","\n","#SBATCH --time=168:00:00","\n","#SBATCH --mail-type=NONE","\n","#SBATCH --error=",script.filepath,".e%J\n","#SBATCH --output=",script.filepath,".o%J","\n",sep="")
#cmd.out<-paste(cmd.out,"/usr/bin/R --vanilla<<EOF\n",sep="")
cmd.out<-"/usr/bin/R --vanilla<<EOF\n"
cmd.out<-paste(cmd.out,"cv.threshold<-",cv.threshold,"\n",sep="")
cmd.out<-paste(cmd.out,"output.dir<-\"",output.dir,"\"\n",sep="")
cmd.out<-paste(cmd.out,"source(\"",dataload.filepath,"\")\n",sep="")
cmd.out<-paste(cmd.out,"EOF\n",sep="")
cat(cmd.out,file=script.filepath,append=F)
system(paste("chmod 700 ",script.filepath,sep=""))
#cat("bsub < ",script.filepath,"\n",sep="",file=jobsub.filepath,append=T)
cat("sbatch ",script.filepath,"\n",sep="",file=jobsub.filepath,append=T)

}
system(paste("chmod 700 ",jobsub.filepath,"\n",sep=""))










# 6.3.1 for each cv.threshold, generate the hclust tree for review by eyes to decide potential heights of cut on the tree
r.filepath<-"/home/yanxiting/Research/GRADS_SARC/Rprogram/WGCNA_unadjusted_ALL_filtering/6_1_sampletree.R"
data.dir<-"/home/yanxiting/Research/GRADS_SARC/Results_summary_BAL_hg38/WGCNA_BAL/unadjusted_ALL_filtering"
#cv.threshold.vect<-c(0,0.2565,0.3148,0.3844,0.5058,0.6651,0.8375,1.0666,1.5233,3.4702)
cv.threshold.vect<-0
script.dir<-file.path(data.dir,"scripts_sampletree_outliercompare")
if(file.exists(script.dir)==F){
dir.create(script.dir)
}

jobsub.filepath<-file.path(script.dir,"jobsub.bat")
if(file.exists(jobsub.filepath)){
file.remove(jobsub.filepath)
}
file.create(jobsub.filepath)

for(i in 1:length(cv.threshold.vect)){
    
cv.threshold<-cv.threshold.vect[i]
script.filepath<-file.path(script.dir,paste("cv",cv.threshold,".sh",sep=""))
#cmd.out<-paste("#BSUB -q kaminski -n 1 -M 8000 -R \"span[ptile=1] rusage[mem=8000]\" -J ",paste("cv",cv.threshold,sep="")," -o ",script.filepath,".o%J -e ",script.filepath,".e%J\n",sep="")
#cmd.out<-paste("#!/bin/bash","\n","#SBATCH --partition=pi_kaminski","\n","#SBATCH --job-name=",paste("cv",cv.threshold,sep=""),"\n","#SBATCH --ntasks=1 --nodes=1 --cpus-per-task=1","\n","#SBATCH --mem=16000","\n","#SBATCH --time=168:00:00","\n","#SBATCH --mail-type=NONE","\n","#SBATCH --error=",script.filepath,".e%J\n","#SBATCH --output=",script.filepath,".o%J","\n",sep="")
#cmd.out<-paste(cmd.out,"/usr/bin/R --vanilla<<EOF\n",sep="")
cmd.out<-"/usr/bin/R --vanilla<<EOF\n"
cmd.out<-paste(cmd.out,"rdata.filepath<-\"",file.path(data.dir,paste("CV",cv.threshold,"_compare",sep=""),"input_data.RData"),"\"\n",sep="")
cmd.out<-paste(cmd.out,"source(\"",r.filepath,"\")\n",sep="")
cmd.out<-paste(cmd.out,"EOF\n",sep="")
cat(cmd.out,file=script.filepath,append=F)
system(paste("chmod 700 ",script.filepath,sep=""))
cat("sbatch ",script.filepath,"\n",sep="",file=jobsub.filepath,append=T)

}
system(paste("chmod 700 ",jobsub.filepath,"\n",sep=""))











# 6.3.2 review the tree from 6.3.1 and generate a file (hclust_cut_heights_list.txt) containing the potential cut heights for each given cv.threshold
#       the first height in hclust_cut_heights_list.txt has to be higher than the biggest height in sampletree_height_list.txt
#       for each cv.threshold and each given cut height, generate the soft power picking figure to pick soft power
r.filepath<-"/home/yanxiting/Research/GRADS_SARC/Rprogram/WGCNA_unadjusted_ALL_filtering/6_2_sftpicking.R"
data.dir<-"/home/yanxiting/Research/GRADS_SARC/Results_summary_BAL_hg38/WGCNA_BAL/unadjusted_ALL_filtering"
cv.threshold.vect<-0

script.dir<-file.path(data.dir,"scripts_sftpicking_outliercompare")
if(file.exists(script.dir)==F){
dir.create(script.dir)
}

jobsub.filepath<-file.path(script.dir,"jobsub.bat")
if(file.exists(jobsub.filepath)){
file.remove(jobsub.filepath)
}
file.create(jobsub.filepath)

for(i in 1:length(cv.threshold.vect)){
cv.threshold<-cv.threshold.vect[i]

output.dir<-file.path(data.dir,paste("CV",cv.threshold,"_compare",sep=""))
rdata.filepath<-file.path(output.dir,"input_data.RData")
cutheight.filepath<-file.path(output.dir,"hclust_cut_heights_list.txt")

script.filepath<-file.path(script.dir,paste("cv",cv.threshold,".sh",sep=""))
#cmd.out<-paste("#BSUB -q kaminski -n 10 -M 64000 -R \"span[ptile=10] rusage[mem=64000]\" -J ",paste("cv",cv.threshold,sep="")," -o ",script.filepath,".o%J -e ",script.filepath,".e%J\n",sep="")
#cmd.out<-paste("#!/bin/bash","\n","#SBATCH --partition=pi_kaminski","\n","#SBATCH --job-name=",paste("cv",cv.threshold,sep=""),"\n","#SBATCH --ntasks=1 --nodes=1 --cpus-per-task=10","\n","#SBATCH --mem=64000","\n","#SBATCH --time=168:00:00","\n","#SBATCH --mail-type=NONE","\n","#SBATCH --error=",script.filepath,".e%J\n","#SBATCH --output=",script.filepath,".o%J","\n",sep="")
#cmd.out<-paste(cmd.out,"/usr/bin/R --vanilla<<EOF\n",sep="")
cmd.out<-"/usr/bin/R --vanilla<<EOF\n"
cmd.out<-paste(cmd.out,"rdata.filepath<-\"",rdata.filepath,"\"\n",sep="")
cmd.out<-paste(cmd.out,"cutheight.filepath<-\"",cutheight.filepath,"\"\n",sep="")
cmd.out<-paste(cmd.out,"output.dir<-\"",output.dir,"\"\n",sep="")
cmd.out<-paste(cmd.out,"source(\"",r.filepath,"\")\n",sep="")
cmd.out<-paste(cmd.out,"EOF\n",sep="")
cat(cmd.out,file=script.filepath,append=F)
system(paste("chmod 700 ",script.filepath,sep=""))
cat("sbatch ",script.filepath,"\n",sep="",file=jobsub.filepath,append=T)

}
system(paste("chmod 700 ",jobsub.filepath,"\n",sep=""))


# 6.3.3 review the soft power picking figure and pick the soft power for final analysis to save it in the file sft_power_list.txt
r.filepath<-"/home/yanxiting/Research/GRADS_SARC/Rprogram/WGCNA_unadjusted_ALL_filtering/6_3_clustering.R"
data.dir<-"/home/yanxiting/Research/GRADS_SARC/Results_summary_BAL_hg38/WGCNA_BAL/unadjusted_ALL_filtering"
#cv.threshold.vect<-c(0,1,1.5,2,2.5,3,3.5,4)
cv.threshold.vect<-0

script.dir<-file.path(data.dir,"scripts_clustering_outliercompare")
if(file.exists(script.dir)==F){
dir.create(script.dir)
}

jobsub.filepath<-file.path(script.dir,"jobsub.bat")
if(file.exists(jobsub.filepath)){
file.remove(jobsub.filepath)
}
file.create(jobsub.filepath)

for(i in 1:length(cv.threshold.vect)){
cv.threshold<-cv.threshold.vect[i]

output.dir<-file.path(data.dir,paste("CV",cv.threshold,"_compare",sep=""))
rdata.filepath<-file.path(output.dir,"sft_picking.RData")
sftpower.filepath<-file.path(output.dir,"sft_power_list.txt")

script.filepath<-file.path(script.dir,paste("cv",cv.threshold,".sh",sep=""))
#cmd.out<-paste("#BSUB -q kaminski -n 10 -M 64000 -R \"span[ptile=10] rusage[mem=64000]\" -J ",paste("cv",cv.threshold,sep="")," -o ",script.filepath,".o%J -e ",script.filepath,".e%J\n",sep="")
#cmd.out<-paste("#!/bin/bash","\n","#SBATCH --partition=pi_kaminski","\n","#SBATCH --job-name=",paste("cv",cv.threshold,sep=""),"\n","#SBATCH --ntasks=1 --nodes=1 --cpus-per-task=10","\n","#SBATCH --mem=64000","\n","#SBATCH --time=168:00:00","\n","#SBATCH --mail-type=NONE","\n","#SBATCH --error=",script.filepath,".e%J\n","#SBATCH --output=",script.filepath,".o%J","\n",sep="")
#cmd.out<-paste(cmd.out,"R --vanilla<<EOF\n",sep="")
cmd.out<-"/usr/bin/R --vanilla<<EOF\n"
cmd.out<-paste(cmd.out,"rdata.filepath<-\"",rdata.filepath,"\"\n",sep="")
cmd.out<-paste(cmd.out,"sftpower.filepath<-\"",sftpower.filepath,"\"\n",sep="")
cmd.out<-paste(cmd.out,"output.dir<-\"",output.dir,"\"\n",sep="")
cmd.out<-paste(cmd.out,"source(\"",r.filepath,"\")\n",sep="")
cmd.out<-paste(cmd.out,"EOF\n",sep="")
cat(cmd.out,file=script.filepath,append=F)
system(paste("chmod 700 ",script.filepath,sep=""))
cat("sbatch ",script.filepath,"\n",sep="",file=jobsub.filepath,append=T)

}
system(paste("chmod 700 ",jobsub.filepath,"\n",sep=""))

The hierarhical clustering tree of all the samples is shown in Figure 1

#wgcna.dir<-output.dir
wgcna.dir<-file.path(home.dir,"scratch/GRADS/SARC_results/Results_summary_PBMC_hg38/baseline/WGCNA/WGCNA_DESeq2Norm_original")

Figure 1:

knitr::include_graphics(file.path(wgcna.dir,"hclust_tree_allsamples.jpg"))

We identified 15 different cutting heights on the hierarchical clustering tree including the height that does not remove any samples. These chosen cutting heights and their corresponding number of identified outliers and the list of outliers are shown in Table 1.

#data.filepath<-"/Users/yanxiting/MyVolumes/GRACE/scratch/GRADS/SARC_results/Results_summary_BAL_hg38/WGCNA_BAL/CV0_compare/cutheight_2_sft.txt"
data.filepath<-file.path(wgcna.dir,"cutheight_2_sft.txt")
temp.matrix<-read.table(data.filepath,sep="\t",header=T,check.names=F,stringsAsFactors=FALSE)
for(i in 1:nrow(temp.matrix)){
  if(i==1){
    next
  }
  temp.names<-temp.matrix[i-1,3]

  if(temp.names!=""){
    temp.names<-paste(temp.names,temp.matrix[i,3],sep=";")
  }else{
    temp.names<-temp.matrix[i,3]
  }
  temp.matrix[i,3]<-temp.names
}

temp.matrix[,2]<-cumsum(temp.matrix[,2])

kable(temp.matrix,digits = 4,row.names=FALSE,caption=table_nums_1(name="wgcna_cutheights_table_1",caption="Table of the chosen cutting heights on the hierarchical clustering tree for the unadjusted data and their corresponding number of outliers and the list of outliers.")) %>% kable_styling(bootstrap_options = c("striped", "hover", "condensed","responsive"), full_width=FALSE,position="left") 
Table 1: Table of the chosen cutting heights on the hierarchical clustering tree for the unadjusted data and their corresponding number of outliers and the list of outliers.
cut_height outlier_number outliers_ids
17173678 0
17173676 15 02S010;03S063;04S016;04S028;05S003;05S004;05S007;05S006;05S014;05S037;06S005;06S006;08S059;09S030;09S040
#write.table(temp.matrix,sep="\t",row.names=F,col.names=T,quote=F)

The WGCNA results of these 7 chosen cutting heights are compared and shown in Figure 2.

Figure 2:

knitr::include_graphics(file.path(wgcna.dir,"dendrogram_outliers_compare.jpg"))

Based on the comparison in Figure 2, we decided to use the cutting height= which defined 15 outliers for the WGCNA analysis. The identified gene modules and their correlation with the given clinical trais are shown in .

We had to redraw the heatmap of correlation between clinical traits and modules.

output.filepath<-file.path(output.dir,paste("heatmap_trait_correlation_modules_outliers_",i,"_pvaluesig_adjsuted.eps",sep=""))
#pdf(file = output.filepath,width = 15,height = 9,paper="special")
#sizeGrWindow(15,9)
postscript(output.filepath,width=14,height=16,paper="special")
#pdf(file="Plots/moduleTraitRelationships.pdf", width=10, height=6);
# Will display correlations and their p-values
textMatrix = paste(signif(moduleTraitCor, 2), "\n(",signif(moduleTraitPvalue, 1), ")", sep = "");
dim(textMatrix) = dim(moduleTraitCor)
par(mar = c(12, 12, 3, 1));
# Display the correlation values within a heatmap plot
temp.moduleTraitPvalue<-moduleTraitPvalue
temp.moduleTraitCor<-moduleTraitCor
temp.moduleTraitCor[temp.moduleTraitPvalue>0.05]<-NA

labeledHeatmap(Matrix = temp.moduleTraitCor,
xLabels = rownames(my.datTraits[]),
yLabels = names(MEs),
ySymbols = names(MEs),
colorLabels = FALSE,
colors = blueWhiteRed(50),
textMatrix = textMatrix,
#cex.text=0.8,
setStdMargins = FALSE,
cex.text = 0.5,
zlim = c(-1,1),
main = paste("Module-trait relationships"),
naColor="white")
#savePlot(output.filepath,type="jpeg")
dev.off()
null device 
          1 

Figure 3:

knitr::include_graphics(file.path(wgcna.dir,"heatmap_trait_correlation_modules_outliers_2_pvaluesig_adjsuted.jpg"))

data.filepath<-file.path(wgcna.dir,"wgcna_cutheights.RData")
load(data.filepath)

The WGCNA analysis identified 67 gene modules and the size of these modules ranges from 15 to 4812. The results in Figure 3 provided the following observations:

  1. Age is only correlated with one gene module (MEwhite). This module is also correlated with.

  1. Gender is correlated with a few gene modules, which are also correlated with BDFVC, BDFEV1 in liters, suggesting the gender effect on the unadjusted PFTs again.
# only demonstrate the gene modules that correlate with age
modules.chosen<-rownames(moduleTraitPvalue)[moduleTraitPvalue[,"GENDER"]<0.05]
my.moduleTraitCor<-moduleTraitCor[modules.chosen,]
my.moduleTraitPvalue<-moduleTraitPvalue[modules.chosen,]

textMatrix = paste(signif(my.moduleTraitCor, 2), "\n(",signif(my.moduleTraitPvalue, 1), ")", sep = "");
dim(textMatrix) = dim(my.moduleTraitCor)
par(mar = c(12, 12, 3, 1));
# Display the correlation values within a heatmap plot
temp.moduleTraitPvalue<-my.moduleTraitPvalue
temp.moduleTraitCor<-my.moduleTraitCor
temp.moduleTraitCor[temp.moduleTraitPvalue>0.05]<-NA

labeledHeatmap(Matrix = temp.moduleTraitCor,
xLabels = rownames(my.datTraits[]),
yLabels = modules.chosen,
ySymbols = modules.chosen,
colorLabels = FALSE,
colors = blueWhiteRed(50),
textMatrix = textMatrix,
#cex.text=0.8,
setStdMargins = FALSE,
cex.text = 0.5,
zlim = c(-1,1),
main = paste("Module-trait relationships (GENDER)"),
naColor="white")

#savePlot(output.filepath,type="jpeg")
  1. RACE is also correlated with a few gene modules, which are also correlated with PFTs in liters.
# only demonstrate the gene modules that correlate with age
modules.chosen<-rownames(moduleTraitPvalue)[moduleTraitPvalue[,"RACE"]<0.05]
my.moduleTraitCor<-moduleTraitCor[modules.chosen,]
my.moduleTraitPvalue<-moduleTraitPvalue[modules.chosen,]

textMatrix = paste(signif(my.moduleTraitCor, 2), "\n(",signif(my.moduleTraitPvalue, 1), ")", sep = "");
dim(textMatrix) = dim(my.moduleTraitCor)
par(mar = c(12, 12, 3, 1));
# Display the correlation values within a heatmap plot
temp.moduleTraitPvalue<-my.moduleTraitPvalue
temp.moduleTraitCor<-my.moduleTraitCor
temp.moduleTraitCor[temp.moduleTraitPvalue>0.05]<-NA

labeledHeatmap(Matrix = temp.moduleTraitCor,
xLabels = rownames(my.datTraits[]),
yLabels = modules.chosen,
ySymbols = modules.chosen,
colorLabels = FALSE,
colors = blueWhiteRed(50),
textMatrix = textMatrix,
#cex.text=0.8,
setStdMargins = FALSE,
cex.text = 0.5,
zlim = c(-1,1),
main = paste("Module-trait relationships (RACE)"),
naColor="white")

#savePlot(output.filepath,type="jpeg")
  1. There are some gene modules that are correlated with many PFTs. Some of them are also correlated with GENDER, RACE. But some of them are also correlated with PFTs independently of GENDER and RACE.
# only demonstrate the gene modules that correlate with age
temp.matrix<-moduleTraitPvalue[,c("BDFVC","FVCPRED","ppfvcpreNhanes","ppfvcpostNhanes","ppfvcpreGli","ppfvcpostGli","BDFEV1","FEV1PRED","ppfevpreNhanes","ppfevpostNhanes","ppfevpreGli","ppfevpostGli","BDDLCO","PREDDLCO")]
temp.matrix<-apply(temp.matrix<0.05,1,sum)
modules.chosen<-rownames(moduleTraitPvalue)[temp.matrix>=1]
my.moduleTraitCor<-moduleTraitCor[modules.chosen,]
my.moduleTraitPvalue<-moduleTraitPvalue[modules.chosen,]

textMatrix = paste(signif(my.moduleTraitCor, 2), "\n(",signif(my.moduleTraitPvalue, 1), ")", sep = "");
dim(textMatrix) = dim(my.moduleTraitCor)
par(mar = c(12, 12, 3, 1));
# Display the correlation values within a heatmap plot
temp.moduleTraitPvalue<-my.moduleTraitPvalue
temp.moduleTraitCor<-my.moduleTraitCor
temp.moduleTraitCor[temp.moduleTraitPvalue>0.05]<-NA

labeledHeatmap(Matrix = temp.moduleTraitCor,
xLabels = rownames(my.datTraits[]),
yLabels = modules.chosen,
ySymbols = modules.chosen,
colorLabels = FALSE,
colors = blueWhiteRed(50),
textMatrix = textMatrix,
#cex.text=0.8,
setStdMargins = FALSE,
cex.text = 0.5,
zlim = c(-1,1),
main = paste("Module-trait relationships (PFTs)"),
naColor="white")

#savePlot(output.filepath,type="jpeg")
LS0tCnRpdGxlOiAiV0dDTkEgb2YgU0FSQyBQQk1DIERFU2VxMiBOb3JtYWxpemVkIFJOQXNlcSBkYXRhIgphdXRob3I6ICJYaXRpbmcgWWFuIgpkYXRlOiAiMDkvMDgvMjAyMSIKb3V0cHV0OgogIGh0bWxfbm90ZWJvb2s6CiAgICBjb2RlX2ZvbGRpbmc6IGhpZGUKICAgIGZpZ19jYXB0aW9uOiB5ZXMKICAgIGhpZ2hsaWdodDogdGFuZ28KICAgIG51bWJlcl9zZWN0aW9uczogbm8KICAgIHRoZW1lOiB1bml0ZWQKICAgIHRvYzogeWVzCiAgICB0b2NfZGVwdGg6IDYKICAgIHRvY19mbG9hdDogeWVzCi0tLQoKYGBge3Igc2V0dXAsIGluY2x1ZGU9RkFMU0V9CmtuaXRyOjpvcHRzX2NodW5rJHNldChldmFsPVRSVUUsZWNobyA9IFRSVUUsY2FjaGU9VFJVRSx3YXJuaW5nPUZBTFNFLG1lc3NhZ2UgPSBGQUxTRSxyZXN1bHRzPSdob2xkJyxjYWNoZS5sYXp5ID0gRkFMU0UpCmtuaXRyOjpvcHRzX2tuaXQkc2V0KGV2YWwuYWZ0ZXIgPSAnZmlnLmNhcCcsZGV2PWMoJ3BuZycsJ3Bvc3RzY3JpcHQnKSkKCmxpYnJhcnkoa2FibGVFeHRyYSkKbGlicmFyeShnZGF0YSkKbGlicmFyeShrbml0cikKbGlicmFyeShjYXB0aW9uZXIpCmxpYnJhcnkobmxtZSkKbGlicmFyeShyZ2wpCmxpYnJhcnkoZ3Bsb3RzKQpsaWJyYXJ5KFdHQ05BKQpsaWJyYXJ5KHhsc3gpCgprbml0X2hvb2tzJHNldCh3ZWJnbCA9IGhvb2tfd2ViZ2wpCgoKdGFibGVfbnVtc18xIDwtIGNhcHRpb25lcjo6Y2FwdGlvbmVyKHByZWZpeD0iVGFibGUiLGxldmVscz0xKQpmaWd1cmVfbnVtc18xPC0gY2FwdGlvbmVyOjpjYXB0aW9uZXIocHJlZml4PSJGaWd1cmUiLGxldmVscz0xKQoKCnRhYmxlX251bXNfMiA8LSBjYXB0aW9uZXI6OmNhcHRpb25lcihwcmVmaXg9IlRhYmxlIixsZXZlbHM9MikKZmlndXJlX251bXNfMjwtIGNhcHRpb25lcjo6Y2FwdGlvbmVyKHByZWZpeD0iRmlndXJlIixsZXZlbHM9MikKCnRhYmxlX251bXNfMyA8LSBjYXB0aW9uZXI6OmNhcHRpb25lcihwcmVmaXg9IlRhYmxlIixsZXZlbHM9MykKZmlndXJlX251bXNfMyA8LSBjYXB0aW9uZXI6OmNhcHRpb25lcihwcmVmaXg9IkZpZ3VyZSIsbGV2ZWxzPTMpCgpob21lLmRpcjwtIi9ob21lL3lhbnhpdGluZy9kcml2ZXJfR3JhY2UiCiNob21lLmRpcjwtIi9ob21lL3h5NDgiCnNvdXJjZShwYXN0ZShob21lLmRpciwiL1Jwcm9ncmFtL215X2Z1bmN0aW9ucy5SIixzZXA9IiIpKQpvdXRwdXQuZGlyPC1maWxlLnBhdGgoaG9tZS5kaXIsInNjcmF0Y2gvR1JBRFMvU0FSQ19yZXN1bHRzL1Jlc3VsdHNfc3VtbWFyeV9QQk1DX2hnMzgiKQpgYGAKCgojIFdHQ05BIG9uIFBCTUMgYmFzZWxpbmUgKG9yaWdpbmFsKQoKRmlyc3QsIHdlIHJ1biBXR0NOQSBvbiB0aGUgb3JpZ2luYWwgREVTZXEyIG5vcm1hbGl6ZWQgZGF0YS4gCgpTYXZlIHRoZSBmb2xsb3dpbmcgY29kZSBibG9jayBhcyBSIGZpbGUgdG8gbG9hZCBpbiBhbmQgbWF0Y2ggdGhlIGNsaW5pY2FsIGRhdGEsZnBrbSBtYXRyaXgsIGFuZCB0aGUgc2FtcGxlIG1hcHBpbmcgbWF0cml4LgpgYGB7ciAwX0NsZWFuRGF0YUxvYWRfREVTZXEyTm9ybV9vcmlnaW5hbC5SLGV2YWw9RkFMU0UsZWNobz1UUlVFLHJlc3VsdHM9J2hpZGUnfQojIC9ob21lL3h5NDgvUnByb2dyYW0vR1JBRFNfUEJNQy9XR0NOQS8wX0NsZWFuRGF0YUxvYWQuUgpob21lLmRpcjwtIi9ob21lL3lhbnhpdGluZy9kcml2ZXJfR3JhY2UiCiNob21lLmRpcjwtIi9ob21lL3h5NDgiCmRhdGEuZGlyPC1maWxlLnBhdGgoaG9tZS5kaXIsInNjcmF0Y2gvR1JBRFMvU0FSQ19yZXN1bHRzL1Jlc3VsdHNfc3VtbWFyeV9QQk1DX2hnMzgiKQpmcGttLmZpbGVwYXRoPC1maWxlLnBhdGgoaG9tZS5kaXIsInNjcmF0Y2gvR1JBRFMvU0FSQ19yZXN1bHRzL1Jlc3VsdHNfc3VtbWFyeV9QQk1DX2hnMzgvYmFzZWxpbmUvZGF0YSIsIkRFU2VxMl9ub3JtYWxpemVkXzI3Nl9jbGVhbi50eHQiKQptYXN0ZXJrZXkuZmlsZXBhdGg8LWZpbGUucGF0aChob21lLmRpciwic2NyYXRjaC9HUkFEUy9TQVJDX3Jlc3VsdHMvRGF0YS9HUkFEUyBNYXN0ZXIgUHJvZ3Jlc3MgS2V5IDYtMTMtMTYueGxzeCIpCgojIGxvYWQgaW4gdGhlIGxpc3Qgb2YgR1JBRFMgSURzIGFuZCBLSVQgSURzIGZyb20gdGhlIG1hc3RlciBrZXkgZmlsZQpta2V5PC1yZWFkLnhscyhtYXN0ZXJrZXkuZmlsZXBhdGgsc2hlZXQ9Myxza2lwPTIsaGVhZGVyPVQsY2hlY2submFtZXM9RixzdHJpbmdzQXNGYWN0b3JzID0gRikKbWtleSA8LSBta2V5WywxOjVdCgojIGxvYWQgaW4gdGhlIGZwa20gbWF0cml4CmZwa20ubWF0cml4PC1yZWFkLnRhYmxlKGZwa20uZmlsZXBhdGgsc2VwPSJcdCIsaGVhZGVyPVQsY2hlY2submFtZXM9Rixhcy5pcz1UUlVFLGNvbW1lbnQuY2hhciA9ICIiKQpmcGttLm1hdHJpeC5hbm5vPC1mcGttLm1hdHJpeFssMTo2XQpmcGttLm1hdHJpeDwtZnBrbS5tYXRyaXhbLDc6bmNvbChmcGttLm1hdHJpeCldCmRhdGEubWF0cml4PC1mcGttLm1hdHJpeAoKIyByZW1vdmUgdGhlIEdSQURTIElEcyB0aGF0IGRvIG5vdCBoYXZlIHRoZSBGUEtNcwp0ZW1wLm5hbWVzPC1jb2xuYW1lcyhmcGttLm1hdHJpeCkKbWF0Y2hlZC5saXN0IDwtIG1rZXlbbWtleVssMV0laW4ldGVtcC5uYW1lcyxdCm1hdGNoZWQubGlzdCRgUGhlbm90eXBlL0dlbm90eXBlYCA8LSBhcy5mYWN0b3IobWF0Y2hlZC5saXN0JGBQaGVub3R5cGUvR2Vub3R5cGVgKQoKIyBnZW5lcmF0ZSBhIG1hdHJpeCB0aGF0IG1hdGNoIHRoZSBjb2x1bW5zIG9mIGRhdGEubWF0cml4CnNhbXBsZS5tYXRyaXg8LW1hdGNoZWQubGlzdApyb3duYW1lcyhzYW1wbGUubWF0cml4KTwtc2FtcGxlLm1hdHJpeFssMV0Kc2FtcGxlLm1hdHJpeDwtc2FtcGxlLm1hdHJpeFtjb2xuYW1lcyhkYXRhLm1hdHJpeCksXQoKYGBgCgoKTG9hZCBpbiBjbGluaWNhbCBtYXRyaXggYW5kIGV4dHJhY3QgZGF0YSBmb3IgdGhlIHZhcmlhYmxlcyBuZWVkZWQgdG8gY29ycmVsYXRlIHdpdGggdGhlIGdlbmUgbW9kdWxlcy4gU29tZSBvZiB0aGUgY2xpbmljYWwgZmVhdHVlcnMgd2VyZSBhbHNvIHJlLWNvZGVkLgpgYGB7ciA2XzBfRGF0YUxvYWRfREVTZXEyTm9ybV9vcmlnaW5hbC5SLGV2YWw9RkFMU0UsZWNobz1UUlVFLHJlc3VsdHM9J2hpZGUnfQojLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLSMgL1Jwcm9ncmFtL0dSQURTX1BCTUMvV0dDTkEvNl8wX0RhdGFMb2FkLlIKIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIwojICBMb2FkIGluIGJvdGggdGhlIGdlbmUgZXhwcmVzc2lvbiBhbmQgdGhlIGNsaW5pY2FsIGRhdGEKIyAgZmlsdGVyIHRoZSBnZW5lcyBiYXNlZCBvbiBnaXZlbiBjdi50aHJlc2hvbGQgYW5kIG1hdGNoIHRoZSBnZW5lIGV4cHJlc3Npb24gZGF0YSBhbmQgdGhlIGNsaW5pY2FsIGRhdGEKI2hvbWUuZGlyPC0iL2hvbWUveHk0OCIKaG9tZS5kaXI8LSIvaG9tZS95YW54aXRpbmcvZHJpdmVyX0dyYWNlIgpzb3VyY2UoZmlsZS5wYXRoKGhvbWUuZGlyLCJScHJvZ3JhbS9HUkFEU19QQk1DL1dHQ05BLzBfQ2xlYW5EYXRhTG9hZF9ERVNlcTJOb3JtX29yaWdpbmFsLlIiKSkKb3V0cHV0LmRpcjwtZmlsZS5wYXRoKGhvbWUuZGlyLCJzY3JhdGNoL0dSQURTL1NBUkNfcmVzdWx0cy9SZXN1bHRzX3N1bW1hcnlfUEJNQ19oZzM4IikKb3V0cHV0LmRpcjwtZmlsZS5wYXRoKGhvbWUuZGlyLCJzY3JhdGNoL0dSQURTL1NBUkNfcmVzdWx0cy9SZXN1bHRzX3N1bW1hcnlfUEJNQ19oZzM4L2Jhc2VsaW5lL1dHQ05BL1dHQ05BX0RFU2VxMk5vcm1fb3JpZ2luYWwiKQoKaWYoZmlsZS5leGlzdHMob3V0cHV0LmRpcik9PUYpewpkaXIuY3JlYXRlKG91dHB1dC5kaXIpCQp9CgojPT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PQojCiMgIExvYWQgaW4gYm90aCB0aGUgZ2VuZSBleHByZXNzaW9uIGFuZCB0aGUgY2xpbmljYWwgZGF0YQojICBGaWx0ZXIgdGhlIGdlbmVzIGJhc2VkIG9uIENWIGFuZCBtYXRjaCB0aGUgZ2VuZSBleHByZXNzaW9uIGFuZCB0aGUgY2xpbmljYWwgZGF0YQojCiM9PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09CiNjbGluaWMuZmlsZXBhdGg8LWZpbGUucGF0aChob21lLmRpciwiL2hvbWUveWFueGl0aW5nL1Jlc2VhcmNoL0dSQURTX1NBUkMvRGF0YS9TQVJDX2NvbWJpbmVfY2xpbmljYWxfZGF0YTIwMTcwODE0LnR4dCIKI2NsaW5pYy5maWxlcGF0aDwtZmlsZS5wYXRoKGhvbWUuZGlyLCJzY3JhdGNoL0dSQURTL1NBUkNfcmVzdWx0cy9SZXN1bHRzX3N1bW1hcnlfUEJNQ19oZzM4L2RhdGEvY2xpbl9kYXRhX3Zpc2l0MV90cnVuY19YWS5jc3YiKQpjbGluaWMuZmlsZXBhdGg8LWZpbGUucGF0aChob21lLmRpciwic2NyYXRjaC9HUkFEUy9TQVJDX3Jlc3VsdHMvUmVzdWx0c19zdW1tYXJ5X1BCTUNfaGczOC9iYXNlbGluZS9kYXRhL2NsaW5pY19tYXRyaXhfbWVyZ2VkLlJEUyIpCiNjbGluaWMubWF0cml4PXJlYWQuY3N2KGNsaW5pYy5maWxlcGF0aCxjaGVjay5uYW1lcz1GLHN0cmluZ3NBc0ZhY3RvcnM9RikKI3Jvd25hbWVzKGNsaW5pYy5tYXRyaXgpPC1hcy5tYXRyaXgoY2xpbmljLm1hdHJpeClbLDFdICMgR1JBRFMgSUQKY2xpbmljLm1hdHJpeDwtcmVhZFJEUyhjbGluaWMuZmlsZXBhdGgscmVmaG9vayA9IE5VTEwpCmNsaW5pYy5tYXRyaXg8LWNsaW5pYy5tYXRyaXhbc2FtcGxlLm1hdHJpeFssMV0sXQoKIyBsb2FkIGluIHRoZSBnbGkgZXF1YXRpb24gUEZUcwpnbGkuZmlsZXBhdGg8LWZpbGUucGF0aChob21lLmRpciwic2NyYXRjaC9HUkFEUy9TQVJDX3Jlc3VsdHMvUmVzdWx0c19zdW1tYXJ5X1BCTUNfaGczOC9iYXNlbGluZS9kYXRhL25oYW5lc19nbGlfYWRqdXN0ZWRwZnQuY3N2IikKZ2xpLmRhdGE8LXJlYWQuY3N2KGdsaS5maWxlcGF0aCxjaGVjay5uYW1lcyA9IEYsc3RyaW5nc0FzRmFjdG9ycyA9IEYpCnJvd25hbWVzKGdsaS5kYXRhKTwtZ2xpLmRhdGFbLDFdCmdsaS5kYXRhPC1nbGkuZGF0YVtyb3duYW1lcyhjbGluaWMubWF0cml4KSxdCgojIG1lcmdlIHRoZSBnbGkgZGF0YSBpbnRvIHRoZSBjbGluaWMubWF0cml4CmNsaW5pYy5tYXRyaXg8LWNiaW5kKGNsaW5pYy5tYXRyaXgsZ2xpLmRhdGFbLDQ6bmNvbChnbGkuZGF0YSldKQoKIyMjIyMjIyMjI2ZvciBCQUwgc2FtcGxlcwojIG9ubHkga2VlcCBnZW5lcyBleHByZXNzZWQgKGZwa20+MC4wMSBpbiA+MTAlIHNhbXBsZXMpIGFjcm9zcyBhbGwgc2FtcGxlcwpiYWwuZnBrbS5tYXRyaXg8LWRhdGEubWF0cml4CmJhbC5jbGluaWMubWF0cml4PC1jbGluaWMubWF0cml4CgpiYWwuZnBrbS5tYXRyaXg8LWJhbC5mcGttLm1hdHJpeFthcHBseShiYWwuZnBrbS5tYXRyaXg+MCwxLHN1bSk+bmNvbChiYWwuZnBrbS5tYXRyaXgpKjAuMSxdCgojIyMjIyMjIyMjIGZpbHRlciBnZW5lcyB1c2luZyBTRD4wIGFuZCBnZW5lcyB1c2luZyBDVj5jdi50aHJlc2hvbGQjIyMjIyMjIyMjIyMjIyMjCnRlbXAuc2Q8LWFwcGx5KGJhbC5mcGttLm1hdHJpeCwxLHNkKQpiYWwuZnBrbS5tYXRyaXggPC0gYmFsLmZwa20ubWF0cml4W3RlbXAuc2Q+MCxdCgojdGVtcC5tZWFuPC1hcHBseShiYWwuZnBrbS5tYXRyaXgsMSxtZWFuKQojdGVtcC5zZDwtYXBwbHkoYmFsLmZwa20ubWF0cml4LDEsc2QpCiN0ZW1wLmN2PC10ZW1wLnNkL3RlbXAubWVhbgoKI215LmRhdGEubWF0cml4PC1iYWwuZnBrbS5tYXRyaXhbdGVtcC5jdj49Y3YudGhyZXNob2xkLF0KbXkuZGF0YS5tYXRyaXg8LWJhbC5mcGttLm1hdHJpeAoKbGlicmFyeShXR0NOQSkKY29sbGVjdEdhcmJhZ2UoKQoKZGF0RXhwcjwtdChteS5kYXRhLm1hdHJpeCkKCiMgcmVtb3ZlIGNlcnRhaW4gY29sdW1ucyBpbiB0aGUgY2xpbmljYWwgZGF0YQojZGF0VHJhaXRzPC1iYWwuY2xpbmljLm1hdHJpeFssLWMoMSw2LDgsMTE6MzMsMzU6MzYsMzg6NDEpXQpkYXRUcmFpdHM8LWJhbC5jbGluaWMubWF0cml4WyxjKCJBR0UiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJHRU5ERVIiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJSQUNFIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAiQkRGVkMiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJGVkNQUkVEIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAicHBmdmNwcmVOaGFuZXMiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJwcGZ2Y3Bvc3ROaGFuZXMiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJwcGZ2Y3ByZUdsaSIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInBwZnZjcG9zdEdsaSIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIkJERkVWMSIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIkZFVjFQUkVEIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAicHBmZXZwcmVOaGFuZXMiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJwcGZldnBvc3ROaGFuZXMiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJwcGZldnByZUdsaSIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInBwZmV2cG9zdEdsaSIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIkJERExDTyIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIlBSRURETENPIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAiZXRob3IiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJudW1fb3JnYW4iLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJ3YmMiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJjZDQiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJjZDgiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJjYWwiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJkMjUiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJkMTI1IiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAiQ1JQIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAic21va2UiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJwa195ciIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInN0ZXJvaWRfYXR2MSIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgImRtYXJkX2F0djEiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJwX21vbm8iLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJwX2VvcyIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInBfbHltcGgiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJwX25ldXQiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJwX2Jhc28iLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJTQ0FERElORyIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIm5vZHVsZV9seW1waF9waGVubyIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIlBIRU5HUlAiKV0KZGF0VHJhaXRzJG51bV9vcmdhbjwtYXMubnVtZXJpYyhkYXRUcmFpdHMkbnVtX29yZ2FuKQoKIyBhZGQgbW9yZSB2YXJpYWJsZXMKIyJldGhvciI6IGJpbmFyeQojIm51bV9vcmdhbiI6IG9yZGluYWwKIyJjZDQiOgojImQxMjUiOgoKI2V4cGVjdGluZzogCiNzbW9raW5nCiNtZWRpY2F0aW9uCgojIGNoYW5nZSBQSEVOR1JQIGFuZCBTQ0FERElORyBpbnRvIG51bWJlcnMKdGVtcC52ZWN0PC1kYXRUcmFpdHNbLCJTQ0FERElORyJdCnRlbXAubnVtPC1hcy5udW1lcmljKGZhY3Rvcih0ZW1wLnZlY3QsbGV2ZWxzPWMoIjAiLCJJIiwiSUkiLCJJSUkiLCJJViIsIk4vQSIpKSkKdGVtcC5udW1bdGVtcC5udW09PTZdPC1OQQpkYXRUcmFpdHNbLCJTQ0FERElORyJdPC10ZW1wLm51bQoKIyByZWNvZGUgUEhFTkdSUCB0byB0cmVhdG1lbnQKdGVtcC52ZWN0PC1kYXRUcmFpdHNbLCJQSEVOR1JQIl0gCnRlbXAubnVtPC1yZXAoMCxsZW5ndGgodGVtcC52ZWN0KSkgIyB1bnRyZWF0ZWQgYWxsIHNhbXBsZXMKdGVtcC5udW1bdGVtcC52ZWN0JWluJWMoIlN0YWdlIElJLUlJSSwgdHJlYXRlZCIsIlN0YWdlIElWLCB0cmVhdGVkIildPC0xCnRlbXAubnVtW3RlbXAudmVjdCVpbiVjKCJNdWx0aS1vcmdhbiIsIkNhcmRpYWMgZGVmaW5pbmcgdGhlcmFweSIpXTwtTkEKCmRhdFRyYWl0czwtY2JpbmQoZGF0VHJhaXRzLHRlbXAubnVtKQpjb2xuYW1lcyhkYXRUcmFpdHMpW25jb2woZGF0VHJhaXRzKV08LSJUUkVBVE1FTlQiCgojIGFkZCB0cmVhdG1lbnQgZm9yIHN0YWdlIElJLUlJSQp0ZW1wLnZlY3Q8LWRhdFRyYWl0c1ssIlBIRU5HUlAiXSAKdGVtcC5udW08LXJlcCgwLGxlbmd0aCh0ZW1wLnZlY3QpKQp0ZW1wLm51bVshdGVtcC52ZWN0JWluJWMoIlN0YWdlIElJLUlJSSwgdW50cmVhdGVkIiwiU3RhZ2UgSUktSUlJLCB0cmVhdGVkIiApXTwtTkEKdGVtcC5udW1bdGVtcC52ZWN0JWluJWMoIlN0YWdlIElJLUlJSSwgdHJlYXRlZCIpXTwtMQoKZGF0VHJhaXRzPC1jYmluZChkYXRUcmFpdHMsdGVtcC5udW0pCmNvbG5hbWVzKGRhdFRyYWl0cylbbmNvbChkYXRUcmFpdHMpXTwtIlRSRUFUTUVOVF9TVEFHRUlJLUlJSSIKCiMgYWRkIHRyZWF0bWVudCBmb3Igc3RhZ2UgSVYKdGVtcC52ZWN0PC1kYXRUcmFpdHNbLCJQSEVOR1JQIl0gCnRlbXAubnVtPC1yZXAoMCxsZW5ndGgodGVtcC52ZWN0KSkKdGVtcC5udW1bIXRlbXAudmVjdCVpbiVjKCJTdGFnZSBJViwgdHJlYXRlZCIsIlN0YWdlIElWLCB1bnRyZWF0ZWQiKV08LU5BCnRlbXAubnVtW3RlbXAudmVjdCVpbiVjKCJTdGFnZSBJViwgdHJlYXRlZCIpXTwtMQoKZGF0VHJhaXRzPC1jYmluZChkYXRUcmFpdHMsdGVtcC5udW0pCmNvbG5hbWVzKGRhdFRyYWl0cylbbmNvbChkYXRUcmFpdHMpXTwtIlRSRUFUTUVOVF9TVEFHRUlWIgoKZGF0VHJhaXRzPC1kYXRUcmFpdHNbLGNvbG5hbWVzKGRhdFRyYWl0cykhPSJQSEVOR1JQIl0KCiMgY2hhbmdlIFJBQ0UgaW50byBhIGJpbmFyeSB2YXJpYWJsZQp0ZW1wLnZlY3Q8LWRhdFRyYWl0c1ssIlJBQ0UiXQp0ZW1wLnZlY3RbIXRlbXAudmVjdCVpbiVjKDAsMSldPC1OQQpkYXRUcmFpdHNbLCJSQUNFIl08LXRlbXAudmVjdAoKIyByZWNvZGUgdGhlIG5vZHVsZSBseW1waGRlbm9wYXRoeSBwaGVub3R5cGUKdGVtcC52ZWN0PC1kYXRUcmFpdHNbLCJub2R1bGVfbHltcGhfcGhlbm8iXQp0ZW1wLm51bTwtYXMubnVtZXJpYyhmYWN0b3IodGVtcC52ZWN0LGxldmVscz1jKCJub25lIiwibHltcGgiLCJtaWNyb25vZHVsZSIsImJvdGgiKSkpCmRhdFRyYWl0c1ssIm5vZHVsZV9seW1waF9waGVubyJdPC10ZW1wLm51bQoKIyBhZGQgbW9yZSBncm91cCBjb21wYXJpc29ucwp0ZW1wLm5hbWVzPC1yb3duYW1lcyhkYXRUcmFpdHMpCmRhdFRyYWl0czwtdChkYXRUcmFpdHMpCgojIGNoYW5nZSB0aGUgbmFtZXMgb2YgdGhlIGNlbGwgZGlmZmVyZW50aWFscwojcm93bmFtZXMoZGF0VHJhaXRzKVtyb3duYW1lcyhkYXRUcmFpdHMpPT0icF9seW1waCJdPC0iTFlNQkFMRCIKI3Jvd25hbWVzKGRhdFRyYWl0cylbcm93bmFtZXMoZGF0VHJhaXRzKT09InBfbW9ubyJdPC0iTU9OT0JBTEQiCiNyb3duYW1lcyhkYXRUcmFpdHMpW3Jvd25hbWVzKGRhdFRyYWl0cyk9PSJwX25ldXQiXTwtIk5FVUJBTEQiCiNyb3duYW1lcyhkYXRUcmFpdHMpW3Jvd25hbWVzKGRhdFRyYWl0cyk9PSJwX2VvcyJdPC0iRU9TQkFMRCIKI3Jvd25hbWVzKGRhdFRyYWl0cylbcm93bmFtZXMoZGF0VHJhaXRzKT09InBfYmFzbyJdPC0iQkFTQkFMRCIKCgoKIyBjaGVjayBmb3IgdGhlIHN1bSBvZiBjZWxsIGRpZmZlcmVudGlhbHMKCmRhdFRyYWl0czwtZGF0VHJhaXRzW2MoCiJBR0UiLAoiR0VOREVSIiwKIlJBQ0UiLAoiQkRGVkMiLAoiRlZDUFJFRCIsCiJwcGZ2Y3ByZU5oYW5lcyIsCiJwcGZ2Y3Bvc3ROaGFuZXMiLAoicHBmdmNwcmVHbGkiLAoicHBmdmNwb3N0R2xpIiwKIkJERkVWMSIsCiJGRVYxUFJFRCIsCiJwcGZldnByZU5oYW5lcyIsCiJwcGZldnBvc3ROaGFuZXMiLAoicHBmZXZwcmVHbGkiLAoicHBmZXZwb3N0R2xpIiwKIkJERExDTyIsCiJQUkVERExDTyIsICMgYWRkIHRoZSBnbGkKImV0aG9yIiwKIm51bV9vcmdhbiIsCiJ3YmMiLAoiY2Q0IiwKIyJjZDgiLCAjIHByb2JhYmx5IG5vdCBpbXBvcnRhbnQKImNhbCIsCiJkMjUiLAoiZDEyNSIsCiJDUlAiLAoic21va2UiLAoicGtfeXIiLAoic3Rlcm9pZF9hdHYxIiwKImRtYXJkX2F0djEiLAoicF9tb25vIiwKInBfZW9zIiwKInBfbHltcGgiLAoicF9uZXV0IiwKInBfYmFzbyIsCiJTQ0FERElORyIsCiJub2R1bGVfbHltcGhfcGhlbm8iCiksXQoKIyBzZXQgdGhlIG5lZ2F0aXZlIHZhbHVlcyBpbiB0aGUgdHJhaXQgbWF0cml4IGludG8gTkFzCmRhdFRyYWl0c1sicF9seW1waCIsZGF0VHJhaXRzWyJwX2x5bXBoIixdPT0yMjUuM108LU5BCmRhdFRyYWl0c1tkYXRUcmFpdHM8MF08LU5BCgojIHNldCB0aGUgY2VsbCBkaWZmZXJlbnRpYWxzIHdpdGggMCBtYWNyb3BoYWdlcyB0byBiZSBOQXMKI2RhdFRyYWl0c1ssKCFpcy5uYShkYXRUcmFpdHNbIkFMVkJBTEQiLF0pKSAmICNkYXRUcmFpdHNbIkFMVkJBTEQiLF09PTBdW2MoIkFMVkJBTEQiLCJFT1NCQUxEIiwiTFlNQkFMRCIsIk5FVUJBTEQiKSxdPC1OQQpzYXZlLmltYWdlKGZpbGUucGF0aChvdXRwdXQuZGlyLCJpbnB1dF9kYXRhLlJEYXRhIikpCgpgYGAKCgpHZW5lcmF0ZSB0aGUgaGllcmFyaGljYWwgY2x1c3RlcmluZyB0cmVlIHVzaW5nIHRoZSBnZW5lcyB0byBpZGVudGlmeSBwb3RlbnRpYWwgb3V0bGllcnMuCmBgYHtyIDZfMV9zYW1wbGV0cmVlLlIsZXZhbD1GQUxTRSxlY2hvPVRSVUUscmVzdWx0cz0naGlkZSd9CiMtLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tCiMgL1Jwcm9ncmFtL0dSQURTX1BCTUMvV0dDTkEvNl8xX3NhbXBsZXRyZWUuUgpyZGF0YS5maWxlcGF0aDwtZmlsZS5wYXRoKG91dHB1dC5kaXIsImlucHV0X2RhdGEuUkRhdGEiKQpsaWJyYXJ5KFdHQ05BKQoKbG9hZChyZGF0YS5maWxlcGF0aCkKc2FtcGxlVHJlZSA9IGhjbHVzdChkaXN0KGRhdEV4cHIpLCBtZXRob2QgPSAiYXZlcmFnZSIpOwoKIyBzYXZlIHRoZSBoaWVyYXJjaGljYWwgdHJlZSBiZWZvcmUgcmVtb3ZpbmcgdGhlIG91dGxpZXJzCm91dHB1dC5maWxlcGF0aDwtZmlsZS5wYXRoKG91dHB1dC5kaXIsImhjbHVzdF90cmVlX2FsbHNhbXBsZXMuZXBzIikKI3NpemVHcldpbmRvdygxMiw5KQojcGRmKGZpbGUgPSBvdXRwdXQuZmlsZXBhdGgpOwpwb3N0c2NyaXB0KG91dHB1dC5maWxlcGF0aCx3aWR0aD0xMCxoZWlnaHQ9MTAscGFwZXI9InNwZWNpYWwiKQpwYXIoY2V4ID0gMC42KTsKcGFyKG1hciA9IGMoMCw0LDIsMCkpCnBsb3Qoc2FtcGxlVHJlZSwgbWFpbiA9ICJTYW1wbGUgY2x1c3RlcmluZyB0byBkZXRlY3Qgb3V0bGllcnMiLCBzdWI9IiIsIHhsYWI9IiIsIGNleC5sYWIgPSAxLjUsCmNleC5heGlzID0gMS41LCBjZXgubWFpbiA9IDIsY2V4PTAuNTUpCiNzYXZlIHBsb3QKZGV2Lm9mZigpCgojIG91dHB1dCB0aGUgaGVpZ2h0cyBvbiB0aGUgdHJlZSBzbyB3ZSBjYW4gZGVjaWRlIHdoaWNoIGhlaWdodHMgdG8gY3V0Cm91dHB1dC5maWxlcGF0aDwtZmlsZS5wYXRoKG91dHB1dC5kaXIsInNhbXBsZXRyZWVfaGVpZ2h0X2xpc3QudHh0IikKY2F0KHNhbXBsZVRyZWUkaGVpZ2h0LHNlcD0iXG4iLGZpbGU9b3V0cHV0LmZpbGVwYXRoLGFwcGVuZD1GKQpgYGAKCgpXZSBuZWVkIHRvIGdvIG92ZXIgdGhlIHNhbXBsZXRyZWVfaGVpZ2h0X2xpc3QudHh0IGFuZCB0aGUgaGNsdXN0ZV90cmVlX2FsbHNhbXBsZXMuZXBzIHRvIGRlY2lkZSB3aGVyZSB0byBjdXQgb24gdGhlIGhpZXJhcmNoaWNhbCBjbHVzdGVyaW5nIHRyZWUuIFRoZSBmaXJzdCBudW1iZXIgaW4gdGhlIGZpbGVzIHNob3VsZCBiZSBhIG51bWJlciBsYXJnZXIgdGhhbiB0aGUgbGFyZ2VzdCBudW1iZXIgaW4gc2FtcGxldHJlZV9oZWlnaHRfbGlzdC50eHQgdG8gbWFrZSBzdXJlIHdlIGFsc28gcnVuIHRoZSBwcm9ncmFtIHdpdGhvdXQgcmVtb3ZpbmcgYW55IHNhbXBsZXMuIFRoZW4gZWFjaCBudW1iZXIgaW4gdGhlIGZpbGUgc2hvdWxkIGJlIHNsaWdodGx5IHNtYWxsZXIgdGhhbiB0aGUgYWN0dWFsIG51bWJlciBpbiBzYW1wbGV0cmVlX2hlaWdodF9saXN0LnR4dCB0byBtYWtlIHN1cmUgd2UgY3V0IHVuZGVybmVhdGggdGhlIGJyYW5jaC4gCgpXZSBuZWVkIHRvIGdlbmVyYXRlIHRoZSBoY2x1c3RfY3V0X2hlaWdodHNfbGlzdC50eHQgdG8gcHJvdmlkZSB0aGUgaGVpZ2h0cyBhdCB3aGljaCB3ZSB3b3VsZCBsaWtlIHRvIGN1dCB0aGUgaGllcmFyY2hpY2FsIGNsdXN0ZXJpbmcgdHJlZS4gTmFtZSBvZiB0aGlzIGZpbGUgY2Fubm90IGJlIGNoYW5nZWQgYW5kIGl0IGhhcyB0byBiZSBzYXZlZCB1bmRlciB0aGUgc2FtZSBmb2xkZXIgd2hlcmUgYWxsIFdHQ05BIHJlc3VsdHMgYXJlIHNhdmVkLgpgYGB7ciA2XzJfc2Z0cGlja2luZ19ERVNlcTJOb3JtX29yaWdpbmFsLlIsZXZhbD1GQUxTRSxlY2hvPVRSVUUscmVzdWx0cz0naGlkZSd9CiMtLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tCiMgL1Jwcm9ncmFtL0dSQURTL0JBTF9maW5hbF9oZzM4XzIwMTgwNzI2L1dHQ05BX3VuYWRqdXN0ZWRfQUxMX2ZpbHRlcmluZy82XzJfc2Z0cGlja2luZy5SCiMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMKIyBsb2FkIGluIGFsbCBjdXQgaGVpZ2h0cyBmb3IgYSBnaXZlbiBjdi50aHJlc2hvbGQsIGdlbmVyYXRlIHRoZSBzb2Z0IHBvd2VyIHBpY2tpbmcgZmlndXJlcyBmb3IgZWFjaCBjdXQgaGVpZ2h0CnJkYXRhLmZpbGVwYXRoPC1maWxlLnBhdGgob3V0cHV0LmRpciwiaW5wdXRfZGF0YS5SRGF0YSIpCmN1dGhlaWdodC5maWxlcGF0aDwtZmlsZS5wYXRoKG91dHB1dC5kaXIsImhjbHVzdF9jdXRfaGVpZ2h0c19saXN0LnR4dCIpCmxpYnJhcnkoV0dDTkEpCmxvYWQocmRhdGEuZmlsZXBhdGgpCmVuYWJsZVdHQ05BVGhyZWFkcyhuVGhyZWFkcyA9IDE1KQojIGxvYWQgaW4gdGhlIGhlaWdodCB2ZWN0b3IKI2hlaWdodC52ZWN0PC1jKDUwMDAwLDQ4MDAwLDM4MDgwLDM3OTcwLDMzNjQwLDI1MTAwLDIzNzAwLDE5MjYwLDE4MjYwKQpoZWlnaHQudmVjdDwtc2NhbihjdXRoZWlnaHQuZmlsZXBhdGgpCgpzYW1wbGVUcmVlID0gaGNsdXN0KGRpc3QoZGF0RXhwciksIG1ldGhvZCA9ICJhdmVyYWdlIikKCm91dGxpZXIubGlzdDwtbGlzdCgpCm91dGxpZXIubGlzdFtbMV1dPC1jaGFyYWN0ZXIoKQpmb3IoaSBpbiAxOmxlbmd0aChoZWlnaHQudmVjdCkpewpjbHVzdCA9IGN1dHJlZVN0YXRpYyhzYW1wbGVUcmVlLCBjdXRIZWlnaHQgPSBoZWlnaHQudmVjdFtpXSwgbWluU2l6ZSA9IDEwKQprZWVwU2FtcGxlcyA9IChjbHVzdD09MSkKb3V0bGllci5saXN0W1tpXV08LXNldGRpZmYocm93bmFtZXMoZGF0RXhwcilbIWtlZXBTYW1wbGVzXSx1bmxpc3Qob3V0bGllci5saXN0KSkKfQoKIyBvdXRwdXQgdGhlIGhlaWdodCBvZiBjdXQsIHRoZSBudW1iZXIgb2Ygb3V0bGllcnMgYW5kIHRoZSBJRHMgb2YgdGhlIG91dGxpZXJzIGludG8gb25lIGZpbGUKY21kLm91dDwtY2JpbmQoaGVpZ2h0LnZlY3QsdW5saXN0KGxhcHBseShvdXRsaWVyLmxpc3QsbGVuZ3RoKSksdW5saXN0KGxhcHBseShvdXRsaWVyLmxpc3QscGFzdGUsY29sbGFwc2U9IjsiKSkpCmNvbG5hbWVzKGNtZC5vdXQpPC1jKCJjdXRfaGVpZ2h0Iiwib3V0bGllcl9udW1iZXIiLCJvdXRsaWVyc19pZHMiKQpvdXRwdXQuZmlsZXBhdGg8LWZpbGUucGF0aChvdXRwdXQuZGlyLCJjdXRoZWlnaHRfMl9zZnQudHh0IikKd3JpdGUudGFibGUoY21kLm91dCxzZXA9Ilx0IixmaWxlPW91dHB1dC5maWxlcGF0aCxhcHBlbmQ9RixxdW90ZT1GLHJvdy5uYW1lcz1GLGNvbC5uYW1lcz1UKQoKCiMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMKIyA2LjEgZm9yIGVhY2ggZ2l2ZW4gY3V0IGhlaWdodCwgZ2VuZXJhdGUgdGhlIHNvZnQgcG93ZXIgcGlja2luZyBmaWd1cmVzIHRvIHBpY2sgc29mdCBwb3dlciBmb3IgZWFjaCBnaXZlbiBjdXQgaGVpZ2h0IAojIHNhdmUgdGhlIHNvZnQgcG93ZXIgcGlja2luZyBmaWd1cmUgaW50byB0aGUgc2FtZSBmaWxlIGZvciByZXZpZXdpbmcKcGRmLmZpbGVwYXRoPC1maWxlLnBhdGgob3V0cHV0LmRpciwic2Z0X3BpY2tpbmcucGRmIikKcGRmKHBkZi5maWxlcGF0aCkKZm9yKGkgaW4gMTpsZW5ndGgob3V0bGllci5saXN0KSl7CiNjbHVzdCA9IGN1dHJlZVN0YXRpYyhzYW1wbGVUcmVlLCBjdXRIZWlnaHQgPSBoZWlnaHQudmVjdFtpXSwgbWluU2l6ZSA9IDEwKQoja2VlcFNhbXBsZXMgPSAoY2x1c3Q9PTEpCm15LmRhdEV4cHIgPSBkYXRFeHByW3NldGRpZmYocm93bmFtZXMoZGF0RXhwciksdW5saXN0KG91dGxpZXIubGlzdFsxOmldKSksIF0KCgojcmVuYW1lIHRoZSBjb2x1bW5zIChyZW1vdmUgYWxsIGFmdGVyIF8gaW4gc2FtcGxlIG5hbWUpClNhcmNhbGVTYW1wbGVzID0gcm93bmFtZXMobXkuZGF0RXhwcikKc2FtcGxlUm93cyA9bWF0Y2goU2FyY2FsZVNhbXBsZXMscm93bmFtZXMoc2FtcGxlLm1hdHJpeCkpOwpzYW1wbGUubWF0cml4Lm5ldz1zYW1wbGUubWF0cml4W3NhbXBsZVJvd3MsXQp0cmFpdFJvd3MgPSBtYXRjaChzYW1wbGUubWF0cml4Lm5ld1ssMV0sY29sbmFtZXMoZGF0VHJhaXRzKSk7Cm15LmRhdFRyYWl0cyA9IGRhdFRyYWl0c1ssdHJhaXRSb3dzXTsKCm5HZW5lcyA9IG5jb2wobXkuZGF0RXhwcikKblNhbXBsZXMgPSBucm93KG15LmRhdEV4cHIpCgpwb3dlcnMgPSBjKGMoMToxMCksIHNlcShmcm9tID0gMTIsIHRvPTIwLCBieT0yKSkKIyBDYWxsIHRoZSBuZXR3b3JrIHRvcG9sb2d5IGFuYWx5c2lzIGZ1bmN0aW9uCnNmdCA9IHBpY2tTb2Z0VGhyZXNob2xkKG15LmRhdEV4cHIsIHBvd2VyVmVjdG9yID0gcG93ZXJzLCB2ZXJib3NlID0gNSkKCgojIHNhdmUgdGhlIHBsb3QgZm9yIHBpY2tpbmcgdGhlIHNvZnQgcG93ZXIKI291dHB1dC5maWxlcGF0aDwtZmlsZS5wYXRoKG91dHB1dC5kaXIsIlNvZnRQb3dlclBpY2suZXBzIikKI3Bvc3RzY3JpcHQob3V0cHV0LmZpbGVwYXRoLHdpZHRoPTksaGVpZ2h0PTUscGFwZXI9InNwZWNpYWwiKQojc2l6ZUdyV2luZG93KDksIDUpCnBhcihtZnJvdyA9IGMoMSwyKSk7CmNleDEgPSAwLjk7CiMgU2NhbGUtZnJlZSB0b3BvbG9neSBmaXQgaW5kZXggYXMgYSBmdW5jdGlvbiBvZiB0aGUgc29mdC10aHJlc2hvbGRpbmcgcG93ZXIKcGxvdChzZnQkZml0SW5kaWNlc1ssMV0sIC1zaWduKHNmdCRmaXRJbmRpY2VzWywzXSkqc2Z0JGZpdEluZGljZXNbLDJdLAp4bGFiPSJTb2Z0IFRocmVzaG9sZCAocG93ZXIpIiwKeWxhYj0iU2NhbGUgRnJlZSBUb3BvbG9neSBNb2RlbCBGaXQsc2lnbmVkIFJeMiIsdHlwZT0ibiIsCm1haW4gPSBwYXN0ZSgiU2NhbGUgaW5kZXBlbmRlbmNlIikpOwp0ZXh0KHNmdCRmaXRJbmRpY2VzWywxXSwgLXNpZ24oc2Z0JGZpdEluZGljZXNbLDNdKSpzZnQkZml0SW5kaWNlc1ssMl0sCmxhYmVscz1wb3dlcnMsY2V4PWNleDEsY29sPSJyZWQiKTsKIyB0aGlzIGxpbmUgY29ycmVzcG9uZHMgdG8gdXNpbmcgYW4gUl4yIGN1dC1vZmYgb2YgaAphYmxpbmUoaD0wLjkwLGNvbD0icmVkIikKIyBNZWFuIGNvbm5lY3Rpdml0eSBhcyBhIGZ1bmN0aW9uIG9mIHRoZSBzb2Z0LXRocmVzaG9sZGluZyBwb3dlcgpwbG90KHNmdCRmaXRJbmRpY2VzWywxXSwgc2Z0JGZpdEluZGljZXNbLDVdLAp4bGFiPSJTb2Z0IFRocmVzaG9sZCAocG93ZXIpIix5bGFiPSJNZWFuIENvbm5lY3Rpdml0eSIsIHR5cGU9Im4iLAptYWluID0gcGFzdGUoIk1lYW4gY29ubmVjdGl2aXR5XG4iLCJvdXRsaWVyLmxpc3RbIixpLCJdIixzZXA9IiIpKQp0ZXh0KHNmdCRmaXRJbmRpY2VzWywxXSwgc2Z0JGZpdEluZGljZXNbLDVdLCBsYWJlbHM9cG93ZXJzLApjZXg9Y2V4MSxjb2w9InJlZCIpCiNvdXRwdXQuZmlsZXBhdGg8LWZpbGUucGF0aChvdXRwdXQuZGlyLCJTb2Z0UG93ZXJQaWNrLmpwZyIpCiNzYXZlUGxvdChvdXRwdXQuZmlsZXBhdGgsdHlwZT0ianBlZyIpCiNkZXYub2ZmKCkKCn0KZGV2Lm9mZigpCgpzYXZlLmltYWdlKGZpbGUucGF0aChvdXRwdXQuZGlyLCJzZnRfcGlja2luZy5SRGF0YSIpKQpgYGAKCgpXZSBuZWVkIHRvIHJldmlldyB0aGUgcGxvdHMgaW4gc2Z0X3BpY2tpbmcucGRmIGFuZCBnZW5lcmF0ZSBhIHRleHQgZmlsZSB3aXRoIGVhY2ggbGluZSBkZXNjcmliaW5nIHRoZSBzb2Z0cG93ZXIgd2Ugd2FudCB0byBzZXQgZm9yIGVhY2ggY3V0IGhlaWdodCB3ZSBwaWNrZWQgYmVmb3JlLiBUaGlzIHRleHQgZmlsZSB3aWxsIGJlIHNhdmVkIGFzIHNmdF9wb3dlcl9saXN0LnR4dC4gVGhlIG51bWJlcnMgd2lsbCBiZSBpbiB0aGUgc2FtZSBvcmRlciBhcyB0aG9zZSBwbG90cyBpbiB0aGUgc2Z0X3BpY2tpbmcucGRmLiAKYGBge3IgNl8zX2NsdXN0ZXJpbmcuUixldmFsPUZBTFNFLGVjaG89VFJVRSxyZXN1bHRzPSdoaWRlJ30KIy0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLQojIC9Vc2Vycy95YW54aXRpbmcvTXlWb2x1bWVzL0dSQUNFL1Jwcm9ncmFtL0dSQURTL0JBTF9maW5hbF9oZzM4XzIwMTgwNzI2L1dHQ05BX3VuYWRqdXN0ZWRfQUxMX2ZpbHRlcmluZy82XzNfY2x1c3RlcmluZy5SCiNvdXRwdXQuZGlyPC1maWxlLnBhdGgoaG9tZS5kaXIsInNjcmF0Y2gvR1JBRFMvU0FSQ19yZXN1bHRzL1Jlc3VsdHNfc3VtbWFyeV9QQk1DX2hnMzgvQ1YwX2NvbXBhcmVfVjIiKQpyZGF0YS5maWxlcGF0aDwtZmlsZS5wYXRoKG91dHB1dC5kaXIsInNmdF9waWNraW5nLlJEYXRhIikKc2Z0cG93ZXIuZmlsZXBhdGg8LWZpbGUucGF0aChvdXRwdXQuZGlyLCJzZnRfcG93ZXJfbGlzdC50eHQiKQpsb2FkKHJkYXRhLmZpbGVwYXRoKQoKbGlicmFyeShXR0NOQSkKZW5hYmxlV0dDTkFUaHJlYWRzKG5UaHJlYWRzID0gMTUpCmxpYnJhcnkoZ3Bsb3RzKQoKCm15LmNvci5kaXN0PC1mdW5jdGlvbih4LG1ldGhvZC5uYW1lPSJzcGVhcm1hbiIpewojIGNhbGN1bGF0ZSB0aGUgY29ycmVsYXRpb24gYmV0d2VlbiByb3dzIGluIHggYW5kIHJldHVybiB0aGUgY29ycmVsYXRpb24gbWF0cml4IGFzIGEgZGlzdGFuY2UgbWF0cml4Cnk8LWNvcih0KHgpLG1ldGhvZD1tZXRob2QubmFtZSkKeVtpcy5uYSh5KV08LSAtMS41CnJldHVybihhcy5kaXN0KDEteSkpCn0KCgojIGZvciBlYWNoIGVsZW1lbnQgaW4gb3V0bGllci5saXN0LCBwaWNrIHBvd2VyIGFuZCBwdXQgdGhlbSBpbiBwb3dlci52ZWN0IGJlbG93IGZvciBmdXJ0aGVyIGFuYWx5c2lzCiNwb3dlci52ZWN0PC1jKDMsNCw1LDMsNCwzLDMsMyw0KQpwb3dlci52ZWN0PC1zY2FuKHNmdHBvd2VyLmZpbGVwYXRoKQoKbmV0Lmxpc3Q8LWxpc3QoKQojIG9idGFpbiB0aGUgV0dDTkEgcmVzdWx0cyBmb3IgZWFjaCBnaXZlbiBzZXQgb2Ygb3V0bGllcnMKZm9yKGkgaW4gMTpsZW5ndGgob3V0bGllci5saXN0KSl7CmdjKCkKY2F0KCJpPSIsaSwiXG4iLHNlcD0iIikKI2NsdXN0ID0gY3V0cmVlU3RhdGljKHNhbXBsZVRyZWUsIGN1dEhlaWdodCA9IGhlaWdodC52ZWN0W2ldLCBtaW5TaXplID0gMTApCiNrZWVwU2FtcGxlcyA9IChjbHVzdD09MSkKbXkuZGF0RXhwciA9IGRhdEV4cHJbc2V0ZGlmZihyb3duYW1lcyhkYXRFeHByKSx1bmxpc3Qob3V0bGllci5saXN0WzE6aV0pKSwgXQoKI3JlbmFtZSB0aGUgY29sdW1ucyAocmVtb3ZlIGFsbCBhZnRlciBfIGluIHNhbXBsZSBuYW1lKQpTYXJjYWxlU2FtcGxlcyA9IHJvd25hbWVzKG15LmRhdEV4cHIpCnNhbXBsZVJvd3MgPW1hdGNoKFNhcmNhbGVTYW1wbGVzLHJvd25hbWVzKHNhbXBsZS5tYXRyaXgpKTsKc2FtcGxlLm1hdHJpeC5uZXc9c2FtcGxlLm1hdHJpeFtzYW1wbGVSb3dzLF0KdHJhaXRSb3dzID0gbWF0Y2goc2FtcGxlLm1hdHJpeC5uZXdbLDFdLGNvbG5hbWVzKGRhdFRyYWl0cykpOwpteS5kYXRUcmFpdHMgPSBkYXRUcmFpdHNbLHRyYWl0Um93c107CgpuR2VuZXMgPSBuY29sKG15LmRhdEV4cHIpCm5TYW1wbGVzID0gbnJvdyhteS5kYXRFeHByKQoKIyBzZWxlY3QgdGhlIHBvd2VyIGJhc2VkIG9uIHRoZSBwbG90IGFib3ZlCnBvd2VyLnNlbGVjdGVkPC1wb3dlci52ZWN0W2ldCm5ldCA9IGJsb2Nrd2lzZU1vZHVsZXMobXkuZGF0RXhwciwgcG93ZXIgPSBwb3dlci5zZWxlY3RlZCwgbWluTW9kdWxlU2l6ZSA9IDEwLCBtYXhCbG9ja1NpemUgPSAzMDAwMCwKcmVhc3NpZ25UaHJlc2hvbGQgPSAwLCBtZXJnZUN1dEhlaWdodCA9IDAuMjUsCm51bWVyaWNMYWJlbHMgPSBUUlVFLCBwYW1SZXNwZWN0c0RlbmRybyA9IEZBTFNFLApzYXZlVE9NcyA9IEZBTFNFLAojc2F2ZVRPTUZpbGVCYXNlID0gcGFzdGUoIkNWIixjdi50aHJlc2hvbGQsIm91dGxpZXIiLGksc2VwPSIiKSwKdmVyYm9zZSA9IDMsblRocmVhZHM9MTUpCm5ldC5saXN0W1tpXV08LW5ldAoKCiMtLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0KIyBwbG90IHRoZSBkaXN0YW5jZSBtYXRyaXggYnkgV0dDTkEKZGlzc1RPTSA9IDEtVE9Nc2ltaWxhcml0eUZyb21FeHByKG15LmRhdEV4cHIsIHBvd2VyID0gcG93ZXIuc2VsZWN0ZWQpCnBsb3RUT00gPSBkaXNzVE9NXjcKZGlhZyhwbG90VE9NKSA9IE5BCgojb3V0cHV0LmZpbGVwYXRoPC1maWxlLnBhdGgob3V0cHV0LmRpcixwYXN0ZSgiaGVhdG1hcF9tb2R1bGVfb3V0bGllcnNfIixpLCIuZXBzIixzZXA9IiIpKQojcG9zdHNjcmlwdChvdXRwdXQuZmlsZXBhdGgsd2lkdGg9MTUsaGVpZ2h0PTE1LHBhcGVyPSJzcGVjaWFsIikKI3NpemVHcldpbmRvdygxNSwxNSkKI3RlbXAuZXJyb3I8LXRyeShUT01wbG90KHBsb3RUT01bbmV0JGdvb2RHZW5lcyxuZXQkZ29vZEdlbmVzXSwgbmV0JGRlbmRyb2dyYW1zW1sxXV0sIGxhYmVsczJjb2xvcnMobmV0JGNvbG9ycylbbmV0JGdvb2RHZW5lc10sCiNtYWluID0gcGFzdGUoIk5ldHdvcmsgaGVhdG1hcCBwbG90IixzZXA9IiIpKSkKCiNvdXRwdXQuZmlsZXBhdGg8LWZpbGUucGF0aChvdXRwdXQuZGlyLHBhc3RlKCJoZWF0bWFwX21vZHVsZXNfQ1Zfbm9fbG9nMl93aXRob3V0X291dGxpZXIiLGN2LnRocmVzaG9sZCwiLmpwZyIsc2VwPSIiKSkKI3NhdmVQbG90KG91dHB1dC5maWxlcGF0aCx0eXBlPSJqcGVnIikKI2Rldi5vZmYoKQoKCiMtLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLQojIHBsb3QgdGhlIGRlbmRyb2dyYW0Kb3V0cHV0LmZpbGVwYXRoPC1maWxlLnBhdGgob3V0cHV0LmRpcixwYXN0ZSgiZGVuZHJvZ3JhbV9tb2R1bGVzX291dGxpZXJzXyIsaSwiLmVwcyIsc2VwPSIiKSkKcG9zdHNjcmlwdChvdXRwdXQuZmlsZXBhdGgsd2lkdGg9MjAsaGVpZ2h0PTEyLHBhcGVyPSJzcGVjaWFsIikKI3NpemVHcldpbmRvdygxMiwgOSkKIyBDb252ZXJ0IGxhYmVscyB0byBjb2xvcnMgZm9yIHBsb3R0aW5nCm1lcmdlZENvbG9ycyA9IGxhYmVsczJjb2xvcnMobmV0JGNvbG9ycykKIyBQbG90IHRoZSBkZW5kcm9ncmFtIGFuZCB0aGUgbW9kdWxlIGNvbG9ycyB1bmRlcm5lYXRoCnBsb3REZW5kcm9BbmRDb2xvcnMobmV0JGRlbmRyb2dyYW1zW1sxXV0sIG1lcmdlZENvbG9yc1tuZXQkYmxvY2tHZW5lc1tbMV1dXSwKIk1vZHVsZVxuIGNvbG9ycyIsCnJvd1RleHQgPSBtZXJnZWRDb2xvcnNbbmV0JGJsb2NrR2VuZXNbWzFdXV0sCmRlbmRyb0xhYmVscyA9IEZBTFNFLCBoYW5nID0gMC4wMywKYWRkR3VpZGUgPSBUUlVFLCBndWlkZUhhbmcgPSAwLjA1LApjZXgubWFpbiA9IDIsIGNleC5heGlzID0gMS40LCBjZXgubGFiID0gMS40LApjZXguY29sb3JMYWJlbHMgPSAxLjIsCm1hckFsbD0gYygwLCA1LCAzLCAzKSkKI3NhdmVQbG90KG91dHB1dC5maWxlcGF0aCx0eXBlPSJqcGVnIikKZGV2Lm9mZigpCgojLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLQojIGNhbGN1bGF0ZSB0aGUgY29ycmVsdGlvbiBiZXR3ZWVuIHRoZSBlaWdlbiBnZW5lcyBvZiB0aGUgbW9kdWxlcyB0byBzZWUgaWYgd2UgbmVlZCB0byBjaGFuZ2UgdGhlIG1lcmdlQ3V0SGVpZ2h0Ck1FTGlzdC50ZXN0PC1tb2R1bGVFaWdlbmdlbmVzKG15LmRhdEV4cHIsY29sb3JzPW1lcmdlZENvbG9ycykKTUVzLnRlc3Q8LU1FTGlzdC50ZXN0JGVpZ2VuZ2VuZXMKTUVEaXNzLnRlc3Q8LTEtY29yKE1Fcy50ZXN0KQpNRVRyZWUudGVzdD1oY2x1c3QoYXMuZGlzdChNRURpc3MudGVzdCksbWV0aG9kPSJhdmVyYWdlIikKCiNzaXplR3JXaW5kb3coNyw2KQpvdXRwdXQuZmlsZXBhdGg8LWZpbGUucGF0aChvdXRwdXQuZGlyLHBhc3RlKCJoY2x1c3RfbW9kdWxlc19vdXRsaWVyc18iLGksIi5lcHMiLHNlcD0iIikpCnBvc3RzY3JpcHQob3V0cHV0LmZpbGVwYXRoLHdpZHRoPTIwLGhlaWdodD0xMixwYXBlcj0ic3BlY2lhbCIpCnBsb3QoTUVUcmVlLnRlc3QsbWFpbj0iQ2x1c3RlcmluZyBvZiBtb2R1bGUgZWlnZW5nZW5lcyIseGxhYj0iIixzdWI9IiIpCmFibGluZShoPTAuMjUsY29sPSJyZWQiKQpkZXYub2ZmKCkKCm91dHB1dC5maWxlcGF0aDwtZmlsZS5wYXRoKG91dHB1dC5kaXIscGFzdGUoImNvcnJlbGF0aW9uX21vZHVsZXNfb3V0bGllcnNfIixpLCIudHh0IixzZXA9IiIpKQpjYXQoIlx0IixmaWxlPW91dHB1dC5maWxlcGF0aCxhcGVuZD1GKQp3cml0ZS50YWJsZSgxLU1FRGlzcy50ZXN0LGZpbGU9b3V0cHV0LmZpbGVwYXRoLGFwcGVuZD1ULHNlcD0iXHQiLHJvdy5uYW1lcz1ULGNvbC5uYW1lcz1ULHF1b3RlPUYpCgojLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0KIyBwbG90IHRoZSBjb3JyZWxhdGlvbiBiZXR3ZWVuIHRoZSBtb2R1bGVzIHdpdGggdGhlIGNsaW5pY2FsIHRyYWl0cwojZGF0RXhwcjwtZGF0YS5tYXRyaXhbY2x1c3RlcmluZy5yZXN1bHQ9PWkscHMubmFtZXNdCiNkYXRUcmFpdHM8LWFzLmRhdGEuZnJhbWUodChteS5jbGluaWMuZGF0YVssY2x1c3RlcmluZy5yZXN1bHQ9PWldKSkKbkdlbmVzID0gbmNvbChteS5kYXRFeHByKTsKblNhbXBsZXMgPSBucm93KG15LmRhdEV4cHIpOwojIFJlY2FsY3VsYXRlIE1FcyB3aXRoIGNvbG9yIGxhYmVscwpNRXMwID0gbW9kdWxlRWlnZW5nZW5lcyhteS5kYXRFeHByLCBsYWJlbHMyY29sb3JzKG5ldCRjb2xvcnMpKSRlaWdlbmdlbmVzCk1FcyA9IG9yZGVyTUVzKE1FczApCmNwID0gYmljb3JBbmRQdmFsdWUoTUVzLCB0KG15LmRhdFRyYWl0cykpCm1vZHVsZVRyYWl0Q29yID0gY3AkYmljb3I7Cm1vZHVsZVRyYWl0UHZhbHVlID0gY3AkcDsKCm91dHB1dC5maWxlcGF0aDwtZmlsZS5wYXRoKG91dHB1dC5kaXIscGFzdGUoImhlYXRtYXBfdHJhaXRfY29ycmVsYXRpb25fbW9kdWxlc19vdXRsaWVyc18iLGksIi5lcHMiLHNlcD0iIikpCiNwZGYoZmlsZSA9IG91dHB1dC5maWxlcGF0aCx3aWR0aCA9IDE1LGhlaWdodCA9IDkscGFwZXI9InNwZWNpYWwiKQpwb3N0c2NyaXB0KG91dHB1dC5maWxlcGF0aCx3aWR0aD0xNSxoZWlnaHQ9MjQscGFwZXI9InNwZWNpYWwiKQojc2l6ZUdyV2luZG93KDE1LDkpCiNwZGYoZmlsZT0iUGxvdHMvbW9kdWxlVHJhaXRSZWxhdGlvbnNoaXBzLnBkZiIsIHdpZHRoPTEwLCBoZWlnaHQ9Nik7CiMgV2lsbCBkaXNwbGF5IGNvcnJlbGF0aW9ucyBhbmQgdGhlaXIgcC12YWx1ZXMKdGV4dE1hdHJpeCA9IHBhc3RlKHNpZ25pZihtb2R1bGVUcmFpdENvciwgMiksICJcbigiLHNpZ25pZihtb2R1bGVUcmFpdFB2YWx1ZSwgMSksICIpIiwgc2VwID0gIiIpOwpkaW0odGV4dE1hdHJpeCkgPSBkaW0obW9kdWxlVHJhaXRDb3IpCnBhcihtYXIgPSBjKDEyLCAxMiwgMywgMSkpOwojIERpc3BsYXkgdGhlIGNvcnJlbGF0aW9uIHZhbHVlcyB3aXRoaW4gYSBoZWF0bWFwIHBsb3QKbGFiZWxlZEhlYXRtYXAoTWF0cml4ID0gbW9kdWxlVHJhaXRDb3IsCnhMYWJlbHMgPSByb3duYW1lcyhteS5kYXRUcmFpdHNbXSksCnlMYWJlbHMgPSBuYW1lcyhNRXMpLAp5U3ltYm9scyA9IG5hbWVzKE1FcyksCmNvbG9yTGFiZWxzID0gRkFMU0UsCmNvbG9ycyA9IGdyZWVuV2hpdGVSZWQoNTApLAp0ZXh0TWF0cml4ID0gdGV4dE1hdHJpeCwKc2V0U3RkTWFyZ2lucyA9IEZBTFNFLApjZXgudGV4dCA9IDAuNSwKemxpbSA9IGMoLTEsMSksCm1haW4gPSBwYXN0ZSgiTW9kdWxlLXRyYWl0IHJlbGF0aW9uc2hpcHMiKSkKI3NhdmVQbG90KG91dHB1dC5maWxlcGF0aCx0eXBlPSJqcGVnIikKZGV2Lm9mZigpCgoKCgpvdXRwdXQuZmlsZXBhdGg8LWZpbGUucGF0aChvdXRwdXQuZGlyLHBhc3RlKCJoZWF0bWFwX3RyYWl0X2NvcnJlbGF0aW9uX21vZHVsZXNfb3V0bGllcnNfIixpLCJfcHZhbHVlc2lnLmVwcyIsc2VwPSIiKSkKI3BkZihmaWxlID0gb3V0cHV0LmZpbGVwYXRoLHdpZHRoID0gMTUsaGVpZ2h0ID0gOSxwYXBlcj0ic3BlY2lhbCIpCiNzaXplR3JXaW5kb3coMTUsOSkKcG9zdHNjcmlwdChvdXRwdXQuZmlsZXBhdGgsd2lkdGg9MTAsaGVpZ2h0PTEwLHBhcGVyPSJzcGVjaWFsIikKI3BkZihmaWxlPSJQbG90cy9tb2R1bGVUcmFpdFJlbGF0aW9uc2hpcHMucGRmIiwgd2lkdGg9MTAsIGhlaWdodD02KTsKIyBXaWxsIGRpc3BsYXkgY29ycmVsYXRpb25zIGFuZCB0aGVpciBwLXZhbHVlcwp0ZXh0TWF0cml4ID0gcGFzdGUoc2lnbmlmKG1vZHVsZVRyYWl0Q29yLCAyKSwgIlxuKCIsc2lnbmlmKG1vZHVsZVRyYWl0UHZhbHVlLCAxKSwgIikiLCBzZXAgPSAiIik7CmRpbSh0ZXh0TWF0cml4KSA9IGRpbShtb2R1bGVUcmFpdENvcikKcGFyKG1hciA9IGMoMTIsIDEyLCAzLCAxKSk7CiMgRGlzcGxheSB0aGUgY29ycmVsYXRpb24gdmFsdWVzIHdpdGhpbiBhIGhlYXRtYXAgcGxvdAp0ZW1wLm1vZHVsZVRyYWl0UHZhbHVlPC1tb2R1bGVUcmFpdFB2YWx1ZQp0ZW1wLm1vZHVsZVRyYWl0Q29yPC1tb2R1bGVUcmFpdENvcgp0ZW1wLm1vZHVsZVRyYWl0Q29yW3RlbXAubW9kdWxlVHJhaXRQdmFsdWU+MC4wNV08LU5BCgpsYWJlbGVkSGVhdG1hcChNYXRyaXggPSB0ZW1wLm1vZHVsZVRyYWl0Q29yLAp4TGFiZWxzID0gcm93bmFtZXMobXkuZGF0VHJhaXRzW10pLAp5TGFiZWxzID0gbmFtZXMoTUVzKSwKeVN5bWJvbHMgPSBuYW1lcyhNRXMpLApjb2xvckxhYmVscyA9IEZBTFNFLApjb2xvcnMgPSBibHVlV2hpdGVSZWQoNTApLAp0ZXh0TWF0cml4ID0gdGV4dE1hdHJpeCwKI2NleC50ZXh0PTAuOCwKc2V0U3RkTWFyZ2lucyA9IEZBTFNFLApjZXgudGV4dCA9IDAuNSwKemxpbSA9IGMoLTEsMSksCm1haW4gPSBwYXN0ZSgiTW9kdWxlLXRyYWl0IHJlbGF0aW9uc2hpcHMiKSwKbmFDb2xvcj0id2hpdGUiKQojc2F2ZVBsb3Qob3V0cHV0LmZpbGVwYXRoLHR5cGU9ImpwZWciKQpkZXYub2ZmKCkKCiMtLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tCiMgb3V0cHV0IHRoZSBtb2R1bGUgbWVtYmVyc2hpcCBmb3IgYWxsIGdlbmVzCm91dHB1dC5maWxlcGF0aDwtZmlsZS5wYXRoKG91dHB1dC5kaXIscGFzdGUoIm1vZHVsZV9hc3NpZ25tZW50X291dGxpZXJzXyIsaSwiLnR4dCIsc2VwPSIiKSkKcHMubmFtZXM8LXJvd25hbWVzKG15LmRhdGEubWF0cml4KQpnZW5lLm5hbWVzPC11bm5hbWUoc2FwcGx5KHBzLm5hbWVzLG15LmVsZW1lbnQucmVtb3ZlLHNwbGl0Y2hhcj0iXyIsaW5kZXg9LTEpKQpjbWQub3V0PC1jYmluZChwcy5uYW1lcyxnZW5lLm5hbWVzLGxhYmVsczJjb2xvcnMobmV0JGNvbG9ycykpCndyaXRlLnRhYmxlKGNtZC5vdXQsZmlsZT1vdXRwdXQuZmlsZXBhdGgsYXBwZW5kPUYsc2VwPSJcdCIscm93Lm5hbWVzPUYsY29sLm5hbWVzPUYscXVvdGU9RikKCiMtLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLQojIG91dHB1dCB0aGUgbW9kdWxlIG1lbWJlcnNoaXAgYW5kIHRoZSBnZW5lIHNpZ25pZmljYW5jZSBmb3IgZWFjaCBtb2R1bGUKbkdlbmVzID0gbmNvbChteS5kYXRFeHByKTsKblNhbXBsZXMgPSBucm93KG15LmRhdEV4cHIpOwojIFJlY2FsY3VsYXRlIE1FcyB3aXRoIGNvbG9yIGxhYmVscwpNRXMwID0gbW9kdWxlRWlnZW5nZW5lcyhteS5kYXRFeHByLCBsYWJlbHMyY29sb3JzKG5ldCRjb2xvcnMpKSRlaWdlbmdlbmVzCk1FcyA9IG9yZGVyTUVzKE1FczApCgpnZW5lTW9kdWxlTWVtYmVyc2hpcCA9IGFzLmRhdGEuZnJhbWUoY29yKG15LmRhdEV4cHIsIE1FcywgdXNlID0gInAiKSk7Ck1NUHZhbHVlID0gYXMuZGF0YS5mcmFtZShjb3JQdmFsdWVTdHVkZW50KGFzLm1hdHJpeChnZW5lTW9kdWxlTWVtYmVyc2hpcCksIG5TYW1wbGVzKSk7CgpnZW5lVHJhaXRTaWduaWZpY2FuY2UgPSBhcy5kYXRhLmZyYW1lKGNvcihteS5kYXRFeHByLCB0KG15LmRhdFRyYWl0cyksIHVzZSA9ICJwIikpOwpHU1B2YWx1ZSA9IGFzLmRhdGEuZnJhbWUoY29yUHZhbHVlU3R1ZGVudChhcy5tYXRyaXgoZ2VuZVRyYWl0U2lnbmlmaWNhbmNlKSwgblNhbXBsZXMpKTsKCiMgb3V0cHV0IHRoZSBnZW5lIG1vZHVsZSBtZW1iZXJzaGlwIHRvZ2V0aGVyIHdpdGggdGhlaXIgcCB2YWx1ZXMKbW9kTmFtZXM9c3Vic3RyaW5nKG5hbWVzKE1FcyksIDMpCm91dHB1dC5zdWJkaXI8LWZpbGUucGF0aChvdXRwdXQuZGlyLHBhc3RlKCJNb2R1bGVNZW1iZXJzaGlwX0dTUHZhbHVlX291dGxpZXJzXyIsaSxzZXA9IiIpKQppZihmaWxlLmV4aXN0cyhvdXRwdXQuc3ViZGlyKT09Ril7CmRpci5jcmVhdGUob3V0cHV0LnN1YmRpcikJCn0KZm9yKGsgaW4gMTpsZW5ndGgobW9kTmFtZXMpKXsKb3V0cHV0LmZpbGVwYXRoPC1maWxlLnBhdGgob3V0cHV0LnN1YmRpcixwYXN0ZSgiTW9kdWxlXyIsbW9kTmFtZXNba10sIl9tZW1iZXJzaGlwX2dzaWcudHh0IixzZXA9IiIpKQpwcy5uYW1lczwtcm93bmFtZXMobXkuZGF0YS5tYXRyaXgpCmdlbmUubmFtZXM8LXVubmFtZShzYXBwbHkocHMubmFtZXMsbXkuZWxlbWVudC5yZW1vdmUsc3BsaXRjaGFyPSJfIixpbmRleD0tMSkpCnRlbXAuY29sb3IuZGVzaWc8LWxhYmVsczJjb2xvcnMobmV0JGNvbG9ycykKY21kLm91dDwtY2JpbmQocHMubmFtZXNbdGVtcC5jb2xvci5kZXNpZz09bW9kTmFtZXNba11dLGdlbmUubmFtZXNbdGVtcC5jb2xvci5kZXNpZz09bW9kTmFtZXNba11dLGdlbmVNb2R1bGVNZW1iZXJzaGlwW3RlbXAuY29sb3IuZGVzaWc9PW1vZE5hbWVzW2tdLGNvbG5hbWVzKGdlbmVNb2R1bGVNZW1iZXJzaGlwKT09cGFzdGUoIk1FIixtb2ROYW1lc1trXSxzZXA9IiIpXSxNTVB2YWx1ZVt0ZW1wLmNvbG9yLmRlc2lnPT1tb2ROYW1lc1trXSxjb2xuYW1lcyhNTVB2YWx1ZSk9PXBhc3RlKCJNRSIsbW9kTmFtZXNba10sc2VwPSIiKV0sZ2VuZVRyYWl0U2lnbmlmaWNhbmNlW3RlbXAuY29sb3IuZGVzaWc9PW1vZE5hbWVzW2tdLF0sR1NQdmFsdWVbdGVtcC5jb2xvci5kZXNpZz09bW9kTmFtZXNba10sXSkKY29sbmFtZXMoY21kLm91dCk8LWMoInBzX25hbWVzIiwiZ2VuZV9uYW1lcyIsIm1vZHVsZV9tZW1iZXJzaGlwIiwibW9kdWxlX21lbWJlcnNoaXBfcHZhbHVlIixwYXN0ZSgiZ3NfIixyb3duYW1lcyhteS5kYXRUcmFpdHMpLHNlcD0iIikscGFzdGUoImdzXyIscm93bmFtZXMobXkuZGF0VHJhaXRzKSwiX3B2YWx1ZSIsc2VwPSIiKSkKd3JpdGUudGFibGUoY21kLm91dCxmaWxlPW91dHB1dC5maWxlcGF0aCxhcHBlbmQ9RixzZXA9Ilx0Iixyb3cubmFtZXM9Rixjb2wubmFtZXM9VCxxdW90ZT1GKQp9CgojLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0KIyBvdXRwdXQgdGhlIGRhdGEgbWF0cml4IGFuZCB0aGUgaGVhdG1hcCBmb3IgdGhlIGdlbmUgZXhwcmVzc2lvbiBsZXZlbHMgaW4gZWFjaCBtb2R1bGUgYW5kIGhpZXJhcmNoaWNhbCBjbHVzdGVpcm5nIG9mIHRoZSBzYW1wbGVzIHVzaW5nIGdlbmVzIGZyb20gZWFjaCBtb2R1bGUgc2VwYXJhdGVseQoKbW9kTmFtZXM9c3Vic3RyaW5nKG5hbWVzKE1FcyksIDMpCm91dHB1dC5zdWJkaXI8LWZpbGUucGF0aChvdXRwdXQuZGlyLHBhc3RlKCJNb2R1bGVDbHVzdGVyaW5nX291dGxpZXJzXyIsaSxzZXA9IiIpKQppZihmaWxlLmV4aXN0cyhvdXRwdXQuc3ViZGlyKT09Ril7CmRpci5jcmVhdGUob3V0cHV0LnN1YmRpcikKfQoKZm9yKGsgaW4gMTpsZW5ndGgobW9kTmFtZXMpKXsKb3V0cHV0LmZpbGVwYXRoPC1maWxlLnBhdGgob3V0cHV0LnN1YmRpcixwYXN0ZSgiTW9kdWxlXyIsbW9kTmFtZXNba10sIl9ub291dGxpZXJzX2Zwa20udHh0IixzZXA9IiIpKQpwcy5uYW1lczwtcm93bmFtZXMobXkuZGF0YS5tYXRyaXgpCmdlbmUubmFtZXM8LXVubmFtZShzYXBwbHkocHMubmFtZXMsbXkuZWxlbWVudC5yZW1vdmUsc3BsaXRjaGFyPSJfIixpbmRleD0tMSkpCnRlbXAuY29sb3IuZGVzaWc8LWxhYmVsczJjb2xvcnMobmV0JGNvbG9ycykKY21kLm91dDwtbXkuZGF0YS5tYXRyaXhbdGVtcC5jb2xvci5kZXNpZz09bW9kTmFtZXNba10scm93bmFtZXMobXkuZGF0RXhwcildCiNjbWQub3V0PC1jbWQub3V0W2hjbHVzdChteS5jb3IuZGlzdChjbWQub3V0KSkkb3JkZXIsXQpjYXQoInRyYWNraW5nX2lkXHQiLGZpbGU9b3V0cHV0LmZpbGVwYXRoLGFwcGVuZD1GKQp3cml0ZS50YWJsZShjbWQub3V0LGZpbGU9b3V0cHV0LmZpbGVwYXRoLGFwcGVuZD1ULHNlcD0iXHQiLHJvdy5uYW1lcz1ULGNvbC5uYW1lcz1ULHF1b3RlPUYpCgojdGVtcC5icmVha3M8LWMoMC4wMSxzZXEoZnJvbT0wLjAxLHRvPTEwLGxlbmd0aD00OTkpLDEwLjEpCm91dHB1dC5maWxlcGF0aDwtZmlsZS5wYXRoKG91dHB1dC5zdWJkaXIscGFzdGUoIkhlYXRtYXBfTW9kdWxlXyIsbW9kTmFtZXNba10sIl9ub291dGxpZXJzX2Zwa21fb3JpZ2luYWwuZXBzIixzZXA9IiIpKQpwb3N0c2NyaXB0KG91dHB1dC5maWxlcGF0aCkKdGVtcC5icmVha3M8LXNlcShmcm9tPWFzLm51bWVyaWMocXVhbnRpbGUoYXMubWF0cml4KGNtZC5vdXQpLHByb2I9MC4wNSkpLHRvPWFzLm51bWVyaWMocXVhbnRpbGUoYXMubWF0cml4KGNtZC5vdXQpLHByb2I9MC45NSkpLGxlbmd0aD01MDEpCmhlYXRtYXAuMihhcy5tYXRyaXgoY21kLm91dCksZGlzdGZ1bj1teS5jb3IuZGlzdCxoY2x1c3RmdW49ZnVuY3Rpb24oZCl7cmV0dXJuKGhjbHVzdChkLG1ldGhvZD0iY29tcGxldGUiKSl9LGJyZWFrcz10ZW1wLmJyZWFrcyxzY2FsZT0ibm9uZSIsUm93dj1UUlVFLENvbHY9VFJVRSxjb2w9Y29sb3JwYW5lbCg1MDAsbG93PSJibGFjayIsaGlnaD0icmVkIiksY2V4Q29sPTEsdHJhY2U9Im5vbmUiLGRlbnNpdHkuaW5mbz0ibm9uZSIsa2V5PVRSVUUsc3lta2V5PUZBTFNFLGtleXNpemU9MC44LGxhYlJvdz0iIixuYS5jb2xvcj0iZ3JleSIsbWFpbj1wYXN0ZSgiTW9kdWxlICIsbW9kTmFtZXNba10sc2VwPSIiKSkKZGV2Lm9mZigpCgpvdXRwdXQuZmlsZXBhdGg8LWZpbGUucGF0aChvdXRwdXQuc3ViZGlyLHBhc3RlKCJIZWF0bWFwX01vZHVsZV8iLG1vZE5hbWVzW2tdLCJfbm9vdXRsaWVyc19mcGttX2NlbnRlcmVkLmVwcyIsc2VwPSIiKSkKcG9zdHNjcmlwdChvdXRwdXQuZmlsZXBhdGgpCmNtZC5vdXQ8LW15Lm5vcm1hbGl6ZShjbWQub3V0KQp0ZW1wLmJyZWFrczwtc2VxKGZyb209YXMubnVtZXJpYyhxdWFudGlsZShhcy5tYXRyaXgoY21kLm91dCkscHJvYj0wLjA1KSksdG89YXMubnVtZXJpYyhxdWFudGlsZShhcy5tYXRyaXgoY21kLm91dCkscHJvYj0wLjk1KSksbGVuZ3RoPTUwMSkKI3RlbXAuaGVhdG1hcDwtaGVhdG1hcC4yKGFzLm1hdHJpeChjbWQub3V0KSxkaXN0ZnVuPW15LmNvci5kaXN0LGJyZWFrcz10ZW1wLmJyZWFrcyxzY2FsZT0ibm9uZSIsUm93dj1UUlVFLENvbHY9VFJVRSxjb2w9Y29sb3JwYW5lbCg1MDAsbG93PSJwdXJwbGUiLGhpZ2g9InllbGxvdyIsbWlkPSJncmV5IiksY2V4Q29sPTEsdHJhY2U9Im5vbmUiLGRlbnNpdHkuaW5mbz0ibm9uZSIsa2V5PVRSVUUsc3lta2V5PUZBTFNFLGtleXNpemU9MC44LGxhYlJvdz0iIixuYS5jb2xvcj0iZ3JleSIsbWFpbj1wYXN0ZSgiTW9kdWxlICIsbW9kTmFtZXNba10sc2VwPSIiKSkKdGVtcC5oZWF0bWFwPC1oZWF0bWFwLjIoYXMubWF0cml4KGNtZC5vdXQpLGRpc3RmdW49bXkuY29yLmRpc3QsaGNsdXN0ZnVuPWZ1bmN0aW9uKGQpe3JldHVybihoY2x1c3QoZCxtZXRob2Q9ImNvbXBsZXRlIikpfSxicmVha3M9dGVtcC5icmVha3Msc2NhbGU9Im5vbmUiLFJvd3Y9VFJVRSxDb2x2PVRSVUUsY29sPWNvbG9ycGFuZWwoNTAwLGxvdz0icHVycGxlIixoaWdoPSJ5ZWxsb3ciLG1pZD0iZ3JleSIpLGNleENvbD0xLHRyYWNlPSJub25lIixkZW5zaXR5LmluZm89Im5vbmUiLGtleT1UUlVFLHN5bWtleT1GQUxTRSxrZXlzaXplPTAuOCxsYWJSb3c9IiIsbmEuY29sb3I9ImdyZXkiLG1haW49cGFzdGUoIk1vZHVsZSAiLG1vZE5hbWVzW2tdLHNlcD0iIikpCmRldi5vZmYoKQoKb3V0cHV0LmZpbGVwYXRoPC1maWxlLnBhdGgob3V0cHV0LnN1YmRpcixwYXN0ZSgiSGNsdXN0VHJlZV9Nb2R1bGVfIixtb2ROYW1lc1trXSwiX25vb3V0bGllcnNfZnBrbV9jZW50ZXJlZC5lcHMiLHNlcD0iIikpCnBvc3RzY3JpcHQob3V0cHV0LmZpbGVwYXRoKQpkIDwtIG15LmNvci5kaXN0KGFzLm1hdHJpeCh0KGNtZC5vdXQpKSkgCiMgYXBwbHkgaGlyYXJjaGljYWwgY2x1c3RlcmluZyAKaGMgPC0gaGNsdXN0KGQsbWV0aG9kPSJjb21wbGV0ZSIpICAgICAKcGxvdChoYyxtYWluPXBhc3RlKCJNb2R1bGUgIixtb2ROYW1lc1trXSxzZXA9IiIpLGNleD0wLjMpIApkZXYub2ZmKCkKCgpvdXRwdXQuZmlsZXBhdGg8LWZpbGUucGF0aChvdXRwdXQuc3ViZGlyLHBhc3RlKCJIY2x1c3RUcmVlSGVhdG1hcDJfTW9kdWxlXyIsbW9kTmFtZXNba10sIl9ub291dGxpZXJzX2Zwa21fY2VudGVyZWQuZXBzIixzZXA9IiIpKQpwb3N0c2NyaXB0KG91dHB1dC5maWxlcGF0aCx3aWR0aD00MCxoZWlnaHQ9MjApCnBsb3QodGVtcC5oZWF0bWFwJGNvbERlbmRyb2dyYW0sbWFpbj1wYXN0ZSgiTW9kdWxlICIsbW9kTmFtZXNba10sc2VwPSIiKSxjZXg9MC4zKSAKZGV2Lm9mZigpCgp9CgoKbW9kTmFtZXM9c3Vic3RyaW5nKG5hbWVzKE1FcyksIDMpCm91dHB1dC5zdWJkaXI8LWZpbGUucGF0aChvdXRwdXQuZGlyLHBhc3RlKCJNb2R1bGVDbHVzdGVyaW5nX291dGxpZXJzXyIsaSxzZXA9IiIpKQppZihmaWxlLmV4aXN0cyhvdXRwdXQuc3ViZGlyKT09Ril7CmRpci5jcmVhdGUob3V0cHV0LnN1YmRpcikKfQoKZm9yKGsgaW4gMTpsZW5ndGgobW9kTmFtZXMpKXsKb3V0cHV0LmZpbGVwYXRoPC1maWxlLnBhdGgob3V0cHV0LnN1YmRpcixwYXN0ZSgiTW9kdWxlXyIsbW9kTmFtZXNba10sIl9BTExfZnBrbS50eHQiLHNlcD0iIikpCnBzLm5hbWVzPC1yb3duYW1lcyhteS5kYXRhLm1hdHJpeCkKZ2VuZS5uYW1lczwtdW5uYW1lKHNhcHBseShwcy5uYW1lcyxteS5lbGVtZW50LnJlbW92ZSxzcGxpdGNoYXI9Il8iLGluZGV4PS0xKSkKdGVtcC5jb2xvci5kZXNpZzwtbGFiZWxzMmNvbG9ycyhuZXQkY29sb3JzKQpjbWQub3V0PC1teS5kYXRhLm1hdHJpeFt0ZW1wLmNvbG9yLmRlc2lnPT1tb2ROYW1lc1trXSxdCiNjbWQub3V0PC1jbWQub3V0W2hjbHVzdChteS5jb3IuZGlzdChjbWQub3V0KSkkb3JkZXIsXQpjYXQoInRyYWNraW5nX2lkXHQiLGZpbGU9b3V0cHV0LmZpbGVwYXRoLGFwcGVuZD1GKQp3cml0ZS50YWJsZShjbWQub3V0LGZpbGU9b3V0cHV0LmZpbGVwYXRoLGFwcGVuZD1ULHNlcD0iXHQiLHJvdy5uYW1lcz1ULGNvbC5uYW1lcz1ULHF1b3RlPUYpCgojdGVtcC5icmVha3M8LWMoMC4wMSxzZXEoZnJvbT0wLjAxLHRvPTEwLGxlbmd0aD00OTkpLDEwLjEpCm91dHB1dC5maWxlcGF0aDwtZmlsZS5wYXRoKG91dHB1dC5zdWJkaXIscGFzdGUoIkhlYXRtYXBfTW9kdWxlXyIsbW9kTmFtZXNba10sIl9BTExfZnBrbV9vcmlnaW5hbC5lcHMiLHNlcD0iIikpCnBvc3RzY3JpcHQob3V0cHV0LmZpbGVwYXRoKQp0ZW1wLmJyZWFrczwtc2VxKGZyb209YXMubnVtZXJpYyhxdWFudGlsZShhcy5tYXRyaXgoY21kLm91dCkscHJvYj0wLjA1KSksdG89YXMubnVtZXJpYyhxdWFudGlsZShhcy5tYXRyaXgoY21kLm91dCkscHJvYj0wLjk1KSksbGVuZ3RoPTUwMSkKaGVhdG1hcC4yKGFzLm1hdHJpeChjbWQub3V0KSxkaXN0ZnVuPW15LmNvci5kaXN0LGhjbHVzdGZ1bj1mdW5jdGlvbihkKXtyZXR1cm4oaGNsdXN0KGQsbWV0aG9kPSJjb21wbGV0ZSIpKX0sYnJlYWtzPXRlbXAuYnJlYWtzLHNjYWxlPSJub25lIixSb3d2PVRSVUUsQ29sdj1UUlVFLGNvbD1jb2xvcnBhbmVsKDUwMCxsb3c9ImJsYWNrIixoaWdoPSJyZWQiKSxjZXhDb2w9MSx0cmFjZT0ibm9uZSIsZGVuc2l0eS5pbmZvPSJub25lIixrZXk9VFJVRSxzeW1rZXk9RkFMU0Usa2V5c2l6ZT0wLjgsbGFiUm93PSIiLG5hLmNvbG9yPSJncmV5IixtYWluPXBhc3RlKCJNb2R1bGUgIixtb2ROYW1lc1trXSxzZXA9IiIpKQojaGVhdG1hcC4yKGNtZC5vdXQsc2NhbGU9InJvdyIsUm93dj1UUlVFLENvbHY9RkFMU0UsY29sPWNvbG9ycGFuZWwoNTAwLGxvdz0iYmx1ZSIsbWlkPSJ3aGl0ZSIsaGlnaD0icmVkIiksY2V4Q29sPTEsdHJhY2U9Im5vbmUiLGRlbnNpdHkuaW5mbz0ibm9uZSIsa2V5c2l6ZT0wLjgsbWFyZ2lucz1jKDcsNS41KSkKZGV2Lm9mZigpCgpvdXRwdXQuZmlsZXBhdGg8LWZpbGUucGF0aChvdXRwdXQuc3ViZGlyLHBhc3RlKCJIZWF0bWFwX01vZHVsZV8iLG1vZE5hbWVzW2tdLCJfQUxMX2Zwa21fY2VudGVyZWQuZXBzIixzZXA9IiIpKQpwb3N0c2NyaXB0KG91dHB1dC5maWxlcGF0aCkKY21kLm91dDwtbXkubm9ybWFsaXplKGNtZC5vdXQpCnRlbXAuYnJlYWtzPC1zZXEoZnJvbT1hcy5udW1lcmljKHF1YW50aWxlKGFzLm1hdHJpeChjbWQub3V0KSxwcm9iPTAuMDUpKSx0bz1hcy5udW1lcmljKHF1YW50aWxlKGFzLm1hdHJpeChjbWQub3V0KSxwcm9iPTAuOTUpKSxsZW5ndGg9NTAxKQp0ZW1wLmhlYXRtYXA8LWhlYXRtYXAuMihhcy5tYXRyaXgoY21kLm91dCksZGlzdGZ1bj1teS5jb3IuZGlzdCxoY2x1c3RmdW49ZnVuY3Rpb24oZCl7cmV0dXJuKGhjbHVzdChkLG1ldGhvZD0iY29tcGxldGUiKSl9LGJyZWFrcz10ZW1wLmJyZWFrcyxzY2FsZT0ibm9uZSIsUm93dj1UUlVFLENvbHY9VFJVRSxjb2w9Y29sb3JwYW5lbCg1MDAsbG93PSJwdXJwbGUiLGhpZ2g9InllbGxvdyIsbWlkPSJncmV5IiksY2V4Q29sPTEsdHJhY2U9Im5vbmUiLGRlbnNpdHkuaW5mbz0ibm9uZSIsa2V5PVRSVUUsc3lta2V5PUZBTFNFLGtleXNpemU9MC44LGxhYlJvdz0iIixuYS5jb2xvcj0iZ3JleSIsbWFpbj1wYXN0ZSgiTW9kdWxlICIsbW9kTmFtZXNba10sc2VwPSIiKSkKI2hlYXRtYXAuMihjbWQub3V0LHNjYWxlPSJyb3ciLFJvd3Y9VFJVRSxDb2x2PUZBTFNFLGNvbD1jb2xvcnBhbmVsKDUwMCxsb3c9ImJsdWUiLG1pZD0id2hpdGUiLGhpZ2g9InJlZCIpLGNleENvbD0xLHRyYWNlPSJub25lIixkZW5zaXR5LmluZm89Im5vbmUiLGtleXNpemU9MC44LG1hcmdpbnM9Yyg3LDUuNSkpCmRldi5vZmYoKQoKCm91dHB1dC5maWxlcGF0aDwtZmlsZS5wYXRoKG91dHB1dC5zdWJkaXIscGFzdGUoIkhjbHVzdFRyZWVfTW9kdWxlXyIsbW9kTmFtZXNba10sIl9BTExfZnBrbV9jZW50ZXJlZC5lcHMiLHNlcD0iIikpCnBvc3RzY3JpcHQob3V0cHV0LmZpbGVwYXRoKQpkIDwtIG15LmNvci5kaXN0KGFzLm1hdHJpeCh0KGNtZC5vdXQpKSkgCiMgYXBwbHkgaGlyYXJjaGljYWwgY2x1c3RlcmluZyAKaGMgPC0gaGNsdXN0KGQsbWV0aG9kPSJjb21wbGV0ZSIpICAgICAKcGxvdChoYyxtYWluPXBhc3RlKCJNb2R1bGUgIixtb2ROYW1lc1trXSxzZXA9IiIpLGNleD0wLjMpIApkZXYub2ZmKCkKCgpvdXRwdXQuZmlsZXBhdGg8LWZpbGUucGF0aChvdXRwdXQuc3ViZGlyLHBhc3RlKCJIY2x1c3RUcmVlSGVhdG1hcDJfTW9kdWxlXyIsbW9kTmFtZXNba10sIl9BTExfZnBrbV9jZW50ZXJlZC5lcHMiLHNlcD0iIikpCnBvc3RzY3JpcHQob3V0cHV0LmZpbGVwYXRoLHdpZHRoPTQwLGhlaWdodD0yMCkKcGxvdCh0ZW1wLmhlYXRtYXAkY29sRGVuZHJvZ3JhbSxtYWluPXBhc3RlKCJNb2R1bGUgIixtb2ROYW1lc1trXSxzZXA9IiIpLGNleD0wLjMpIApkZXYub2ZmKCkKCn0KCgoKIy0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tCiMgb3V0cHV0IGEgdGFibGUgdG8gc2hvdyB0aGUgbnVtYmVyIG9mIGdlbmVzIGZvciBlYWNoIG1vZHVsZQpvdXRwdXQuZmlsZXBhdGg8LWZpbGUucGF0aChvdXRwdXQuZGlyLHBhc3RlKCJnZW5lbnVtX3Blcl9tb2R1bGVfb3V0bGllcnNfIixpLCIudHh0IixzZXA9IiIpKQpwcy5uYW1lczwtcm93bmFtZXMobXkuZGF0YS5tYXRyaXgpCmdlbmUubmFtZXM8LXVubmFtZShzYXBwbHkocHMubmFtZXMsbXkuZWxlbWVudC5yZW1vdmUsc3BsaXRjaGFyPSJfIixpbmRleD0tMSkpCnRlbXAuY29sb3IuZGVzaWc8LWxhYmVsczJjb2xvcnMobmV0JGNvbG9ycykKY21kLm91dDwtYXMubWF0cml4KHRhYmxlKHRlbXAuY29sb3IuZGVzaWcpKQpjYXQoIm1vZHVsZV9uYW1lc1x0Z2VuZV9udW1cbiIsZmlsZT1vdXRwdXQuZmlsZXBhdGgsYXBwZW5kPUYpCndyaXRlLnRhYmxlKGNtZC5vdXQsZmlsZT1vdXRwdXQuZmlsZXBhdGgsYXBwZW5kPVQsc2VwPSJcdCIsY29sLm5hbWVzPUYscm93Lm5hbWVzPVQscXVvdGU9RikKCn0KCgoKCgoKCgoKCgoKIyB2aXN1YWxpemUgYW5kIG1lYXN1cmUgdGhlIHNpbWlsYXJpdHkgYmV0d2VlbiBkaWZmZXJlbnQgY2x1c3RlcmluZyByZXN1bHRzCm1vZHVsZS5sYWJlbHMubGlzdDwtbGlzdCgpCm1vZHVsZS5jb2xvcnMubGlzdDwtbGlzdCgpCgptb2R1bGUubGFiZWxzLmxpc3RbWzFdXTwtbmV0Lmxpc3RbWzFdXSRjb2xvcnMKbW9kdWxlLmNvbG9ycy5saXN0W1sxXV08LWxhYmVsczJjb2xvcnMobW9kdWxlLmxhYmVscy5saXN0W1sxXV0pCgpmb3IoaSBpbiAyOmxlbmd0aChuZXQubGlzdCkpewptb2R1bGUubGFiZWxzLmxpc3RbW2ldXTwtbWF0Y2hMYWJlbHMobmV0Lmxpc3RbW2ldXSRjb2xvcnMsbW9kdWxlLmxhYmVscy5saXN0W1sxXV0pCm1vZHVsZS5jb2xvcnMubGlzdFtbaV1dPC1sYWJlbHMyY29sb3JzKG1vZHVsZS5sYWJlbHMubGlzdFtbaV1dKQp9CgojIHJlbW92ZSBnZW5lcyB0aGF0IHdlcmUgY29uc2lkZXJlZCBhcyBiYWQgZ2VuZXMgYnkgbmV0Lmxpc3RbWzFdXQpteS5tb2R1bGUuY29sb3JzLmxpc3Q8LWxpc3QoKQpmb3IoaSBpbiAxOmxlbmd0aChuZXQubGlzdCkpewpteS5tb2R1bGUuY29sb3JzLmxpc3RbW2ldXTwtbW9kdWxlLmNvbG9ycy5saXN0W1tpXV1bbmV0Lmxpc3RbWzFdXSRnb29kR2VuZXNdCn0KCmdlbmVUcmVlID0gbmV0Lmxpc3RbWzFdXSRkZW5kcm9ncmFtc1tbMV1dCm91dHB1dC5maWxlcGF0aDwtZmlsZS5wYXRoKG91dHB1dC5kaXIsImRlbmRyb2dyYW1fb3V0bGllcnNfY29tcGFyZS5lcHMiKQpwb3N0c2NyaXB0KG91dHB1dC5maWxlcGF0aCx3aWR0aD0xMixoZWlnaHQ9MjQscGFwZXI9InNwZWNpYWwiKQojcG9zdHNjcmlwdChvdXRwdXQuZmlsZXBhdGgpCnBsb3REZW5kcm9BbmRDb2xvcnMoZ2VuZVRyZWUsCgkJCQltYXRyaXgodW5saXN0KG15Lm1vZHVsZS5jb2xvcnMubGlzdCksbnJvdz1sZW5ndGgobXkubW9kdWxlLmNvbG9ycy5saXN0W1sxXV0pLGJ5cm93PUYpLAoJCQkJY3Vtc3VtKHVubGlzdChsYXBwbHkob3V0bGllci5saXN0LGxlbmd0aCkpKSwKICAgICAgICAgICAgICAgICAgbWFpbiA9ICJjb21wYXJpbmcgZ2VuZSBkZW5kcm9ncmFtIiwKICAgICAgICAgICAgICAgICAgZGVuZHJvTGFiZWxzID0gRkFMU0UsIGhhbmcgPSAwLjAzLAogICAgICAgICAgICAgICAgICBhZGRHdWlkZSA9IFRSVUUsIGd1aWRlSGFuZyA9IDAuMDUpCmRldi5vZmYoKQoKc2F2ZS5pbWFnZShmaWxlLnBhdGgob3V0cHV0LmRpciwid2djbmFfY3V0aGVpZ2h0cy5SRGF0YSIpKQpgYGAKCgpIZXJlJ3MgdGhlIGNvZGUgYmxvY2sgdGhhdCBnZW5lcmF0ZSB0aGUgc2NyaXB0IGZpbGVzIHRvIHJ1biB0aGVzZSBqb2JzIG9uIEhQQyBpbiBwYXJhbGxlbC4KYGBge3IgNl9XR0NOQV9uZXcuUixldmFsPUZBTFNFLGVjaG89VFJVRSxyZXN1bHRzPSdoaWRlJ30KIy0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLQojIC9Vc2Vycy95YW54aXRpbmcvTXlWb2x1bWVzL0dSQUNFL1Jwcm9ncmFtL0dSQURTL0JBTF9maW5hbF9oZzM4XzIwMTgwNzI2L1dHQ05BX3VuYWRqdXN0ZWRfQUxMX2ZpbHRlcmluZy82X1dHQ05BX25ldy5SCiMjIyMjIyMjIyMjIyMjIyMjIyMjCiMgNi4zICBmb3IgdGhlIHNldCBvZiBwb3RlbnRpYWwgb3V0bGllciwgZXZhbHVhdGUgdGhlIGVmZmVjdCBvZiB0aGVzZSBvdXRsaWVycyBvbiB0aGUgV0dDTkEgcmVzdWx0cwoKIyBnZW5lcmF0ZSB0aGUgZmlsZSB0byBzaG93IHRoZSBDViBxdWFudGlsZXMuCiNkYXRhbG9hZC5maWxlcGF0aDwtIi9ob21lL2Zhcy9rYW1pbnNraS94eTQ4L1Jwcm9ncmFtL0dSQURTL0JBTF9maW5hbF8yMDE3MDcyNC9XR0NOQV9QSEdSUGZpbHRlcmluZy82XzBfRGF0YUxvYWQuUiIKI2RhdGFsb2FkLmZpbGVwYXRoPC0iL2hvbWUvZmFzL2thbWluc2tpL3h5NDgvUnByb2dyYW0vR1JBRFMvQkFMX2ZpbmFsXzIwMTcwNzI0L1dHQ05BX3VuYWRqdXN0ZWRfQUxMX2ZpbHRlcmluZy82XzBfRGF0YUxvYWQuUiIKZGF0YWxvYWQuZmlsZXBhdGg8LSIvaG9tZS95YW54aXRpbmcvUmVzZWFyY2gvR1JBRFNfU0FSQy9ScHJvZ3JhbS9XR0NOQV91bmFkanVzdGVkX0FMTF9maWx0ZXJpbmcvNl8wX0RhdGFMb2FkLlIiCm91dHB1dC5kaXI8LSIvaG9tZS95YW54aXRpbmcvUmVzZWFyY2gvR1JBRFNfU0FSQy9SZXN1bHRzX3N1bW1hcnlfQkFMX2hnMzgvV0dDTkFfQkFML3VuYWRqdXN0ZWRfQUxMX2ZpbHRlcmluZyIKCmlmKGZpbGUuZXhpc3RzKG91dHB1dC5kaXIpPT1GKXsKZGlyLmNyZWF0ZShvdXRwdXQuZGlyKQkKfQoKY3YudGhyZXNob2xkPC0wCgpzb3VyY2UoIi9ob21lL3lhbnhpdGluZy9SZXNlYXJjaC9HUkFEU19TQVJDL1Jwcm9ncmFtL1dHQ05BX3VuYWRqdXN0ZWRfQUxMX2ZpbHRlcmluZy8wX0NsZWFuRGF0YUxvYWQuUiIpCm91dHB1dC5kaXI8LWZpbGUucGF0aChvdXRwdXQuZGlyLHBhc3RlKCJDViIsY3YudGhyZXNob2xkLCJfY29tcGFyZSIsc2VwPSIiKSkKCmlmKGZpbGUuZXhpc3RzKG91dHB1dC5kaXIpPT1GKXsKZGlyLmNyZWF0ZShvdXRwdXQuZGlyKQkKfQoKIz09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT0KIwojICBMb2FkIGluIGJvdGggdGhlIGdlbmUgZXhwcmVzc2lvbiBhbmQgdGhlIGNsaW5pY2FsIGRhdGEKIyAgRmlsdGVyIHRoZSBnZW5lcyBiYXNlZCBvbiBDViBhbmQgbWF0Y2ggdGhlIGdlbmUgZXhwcmVzc2lvbiBhbmQgdGhlIGNsaW5pY2FsIGRhdGEKIwojPT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PQoKI2NsaW5pYy5maWxlcGF0aDwtIi9ob21lL2Zhcy9rYW1pbnNraS94eTQ4L3NjcmF0Y2gvR1JBRFMvU0FSQ19yZXN1bHRzL0RhdGEvU0FSQ19jb21iaW5lX2NsaW5pY2FsX2RhdGEyMDE3MDgxNC50eHQiCmNsaW5pYy5maWxlcGF0aDwtIi9ob21lL3lhbnhpdGluZy9SZXNlYXJjaC9HUkFEU19TQVJDL0RhdGEvU0FSQ19jb21iaW5lX2NsaW5pY2FsX2RhdGEyMDE3MDgxNC50eHQiCmNsaW5pYy5tYXRyaXg9cmVhZC50YWJsZShjbGluaWMuZmlsZXBhdGgsaGVhZGVyPVQsc2VwPSJcdCIsY2hlY2submFtZXM9RixzdHJpbmdzQXNGYWN0b3JzPUYscXVvdGUgPSAiIikKcm93bmFtZXMoY2xpbmljLm1hdHJpeCk8LWFzLm1hdHJpeChjbGluaWMubWF0cml4KVssMV0gIyBHUkFEUyBJRApjbGluaWMubWF0cml4PC1jbGluaWMubWF0cml4W3NhbXBsZS5tYXRyaXhbLDFdLF0KCiMjIyMjIyMjIyNmb3IgQkFMIHNhbXBsZXMKIyBvbmx5IGtlZXAgZ2VuZXMgZXhwcmVzc2VkIChmcGttPjAuMDEgaW4gPjEwJSBzYW1wbGVzKSBhY3Jvc3MgYWxsIHNhbXBsZXMKYmFsLmZwa20ubWF0cml4PC1kYXRhLm1hdHJpeFssc3Vic3RyKGNvbG5hbWVzKGRhdGEubWF0cml4KSwzLDMpPT0iQiJdCmJhbC5jbGluaWMubWF0cml4PC1jbGluaWMubWF0cml4W3N1YnN0cihjb2xuYW1lcyhkYXRhLm1hdHJpeCksMywzKT09IkIiLF0KCmJhbC5mcGttLm1hdHJpeDwtYmFsLmZwa20ubWF0cml4W2FwcGx5KGJhbC5mcGttLm1hdHJpeD4wLjAxLDEsc3VtKT5uY29sKGJhbC5mcGttLm1hdHJpeCkqMC4xLF0gIyAzMDEzMCBnZW5lcyByZWR1Y2VkIHRvIDIwODI4IGdlbmVzCgojIyMjIyMjIyMjIGZpbHRlciBnZW5lcyB1c2luZyBTRD4wIGFuZCBnZW5lcyB1c2luZyBDVj5jdi50aHJlc2hvbGQjIyMjIyMjIyMjIyMjIyMjCgp0ZW1wLnNkPC1hcHBseShiYWwuZnBrbS5tYXRyaXgsMSxzZCkKYmFsLmZwa20ubWF0cml4IDwtIGJhbC5mcGttLm1hdHJpeFt0ZW1wLnNkPjAsXQoKdGVtcC5tZWFuPC1hcHBseShiYWwuZnBrbS5tYXRyaXgsMSxtZWFuKQp0ZW1wLnNkPC1hcHBseShiYWwuZnBrbS5tYXRyaXgsMSxzZCkKdGVtcC5jdjwtdGVtcC5zZC90ZW1wLm1lYW4KCm15LmRhdGEubWF0cml4PC1iYWwuZnBrbS5tYXRyaXhbdGVtcC5jdj49Y3YudGhyZXNob2xkLF0KCgojIG91dHB1dCB0aGUgcHJvcG9ydGlvbiwgdGhlIG51bWJlciBvZiBnZW5lcyByZW1haW4gYW5kIHRoZSBxdWFudGlsZXMgb2YgdGhlIENWIGludG8gb25lIGZpbGUKY2F0KCJwZXJjZW50aWxlXHQjIG9mIGdlbmVzXHRDVl9xdWFudGlsZVxuIixmaWxlPWZpbGUucGF0aChvdXRwdXQuZGlyLCIvLi4vYmFsX2N2X3F1YW50aWxlcy50eHQiKSxhcHBlbmQ9RikKd3JpdGUudGFibGUoY2JpbmQoc2VxKGZyb209MCx0bz0xLGJ5PTAuMDEpLGxlbmd0aCh0ZW1wLmN2KSooMS1zZXEoZnJvbT0wLHRvPTEsYnk9MC4wMSkpLHF1YW50aWxlKHRlbXAuY3YscHJvYj1zZXEoZnJvbT0wLHRvPTEsYnk9MC4wMSkpKSxmaWxlPWZpbGUucGF0aChvdXRwdXQuZGlyLCIvLi4vYmFsX2N2X3F1YW50aWxlcy50eHQiKSxzZXA9Ilx0IixhcHBlbmQ9VCxxdW90ZT1GLHJvdy5uYW1lcz1GLGNvbC5uYW1lcz1GKQoKCgoKCgoKCgoKCgoKCiMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjCiMgNi4zLjAgbG9hZCB0aGUgZGF0YSBpbnRvIFIgYW5kIHNhdmUgdGhlIHdvcmtzcGFjZSBmb3IgZnVydGhlciBhbmFseXNpcwojZGF0YWxvYWQuZmlsZXBhdGg8LSIvaG9tZS9mYXMva2FtaW5za2kveHk0OC9ScHJvZ3JhbS9HUkFEUy9CQUxfZmluYWxfMjAxNzA3MjQvV0dDTkFfdW5hZGp1c3RlZF9BTExfZmlsdGVyaW5nLzZfMF9EYXRhTG9hZC5SIgpkYXRhbG9hZC5maWxlcGF0aDwtIi9ob21lL3lhbnhpdGluZy9SZXNlYXJjaC9HUkFEU19TQVJDL1Jwcm9ncmFtL1dHQ05BX3VuYWRqdXN0ZWRfQUxMX2ZpbHRlcmluZy82XzBfRGF0YUxvYWQuUiIKIyBjaG9vc2UgdGhlIGZvbGxvd2luZyBDViB0aHJlc2hvbGQgdXNpbmcgdGhlIGJhbF9xdWFudGlsZS50eHQgZ2VuZXJhdGVkIGFib3ZlCiNjdi50aHJlc2hvbGQudmVjdDwtYygwLDAuMjM2MCwwLjI4ODgsMC4zNDgzLDAuNDM4NCwwLjU3MTcsMC43MDE5LDAuODUzNywxLjA1NzEsMS40NjE1KQpjdi50aHJlc2hvbGQudmVjdDwtMAojb3V0cHV0LmRpcjwtIi9ob21lL2Zhcy9rYW1pbnNraS94eTQ4L3NjcmF0Y2gvR1JBRFMvU0FSQ19yZXN1bHRzL1Jlc3VsdHNfc3VtbWFyeS9XR0NOQV9CQUwvdW5hZGp1c3RlZF9BTExfZmlsdGVyaW5nIgpvdXRwdXQuZGlyPC0iL2hvbWUveWFueGl0aW5nL1Jlc2VhcmNoL0dSQURTX1NBUkMvUmVzdWx0c19zdW1tYXJ5X0JBTF9oZzM4L1dHQ05BX0JBTC91bmFkanVzdGVkX0FMTF9maWx0ZXJpbmciCgpzY3JpcHQuZGlyPC1maWxlLnBhdGgob3V0cHV0LmRpciwic2NyaXB0c19kYXRhbG9hZF9vdXRsaWVyY29tcGFyZSIpCmlmKGZpbGUuZXhpc3RzKHNjcmlwdC5kaXIpPT1GKXsKZGlyLmNyZWF0ZShzY3JpcHQuZGlyKQp9Cgpqb2JzdWIuZmlsZXBhdGg8LWZpbGUucGF0aChzY3JpcHQuZGlyLCJqb2JzdWIuYmF0IikKaWYoZmlsZS5leGlzdHMoam9ic3ViLmZpbGVwYXRoKSl7CmZpbGUucmVtb3ZlKGpvYnN1Yi5maWxlcGF0aCkKfQpmaWxlLmNyZWF0ZShqb2JzdWIuZmlsZXBhdGgpCgpmb3IoaSBpbiAxOmxlbmd0aChjdi50aHJlc2hvbGQudmVjdCkpewoJCmN2LnRocmVzaG9sZDwtY3YudGhyZXNob2xkLnZlY3RbaV0Kc2NyaXB0LmZpbGVwYXRoPC1maWxlLnBhdGgoc2NyaXB0LmRpcixwYXN0ZSgiY3YiLGN2LnRocmVzaG9sZCwiLnNoIixzZXA9IiIpKQojY21kLm91dDwtcGFzdGUoIiNCU1VCIC1xIGthbWluc2tpIC1uIDEgLU0gODAwMCAtUiBcInNwYW5bcHRpbGU9MV0gcnVzYWdlW21lbT04MDAwXVwiIC1KICIscGFzdGUoImN2Iixjdi50aHJlc2hvbGQsc2VwPSIiKSwiIC1vICIsc2NyaXB0LmZpbGVwYXRoLCIubyVKIC1lICIsc2NyaXB0LmZpbGVwYXRoLCIuZSVKXG4iLHNlcD0iIikKI2NtZC5vdXQ8LXBhc3RlKCIjIS9iaW4vYmFzaCIsIlxuIiwiI1NCQVRDSCAtLXBhcnRpdGlvbj1waV9rYW1pbnNraSIsIlxuIiwiI1NCQVRDSCAtLWpvYi1uYW1lPSIscGFzdGUoImN2Iixjdi50aHJlc2hvbGQsc2VwPSIiKSwiXG4iLCIjU0JBVENIIC0tbnRhc2tzPTEgLS1ub2Rlcz0xIC0tY3B1cy1wZXItdGFzaz0xIiwiXG4iLCIjU0JBVENIIC0tbWVtPTE2MDAwIiwiXG4iLCIjU0JBVENIIC0tdGltZT0xNjg6MDA6MDAiLCJcbiIsIiNTQkFUQ0ggLS1tYWlsLXR5cGU9Tk9ORSIsIlxuIiwiI1NCQVRDSCAtLWVycm9yPSIsc2NyaXB0LmZpbGVwYXRoLCIuZSVKXG4iLCIjU0JBVENIIC0tb3V0cHV0PSIsc2NyaXB0LmZpbGVwYXRoLCIubyVKIiwiXG4iLHNlcD0iIikKI2NtZC5vdXQ8LXBhc3RlKGNtZC5vdXQsIi91c3IvYmluL1IgLS12YW5pbGxhPDxFT0ZcbiIsc2VwPSIiKQpjbWQub3V0PC0iL3Vzci9iaW4vUiAtLXZhbmlsbGE8PEVPRlxuIgpjbWQub3V0PC1wYXN0ZShjbWQub3V0LCJjdi50aHJlc2hvbGQ8LSIsY3YudGhyZXNob2xkLCJcbiIsc2VwPSIiKQpjbWQub3V0PC1wYXN0ZShjbWQub3V0LCJvdXRwdXQuZGlyPC1cIiIsb3V0cHV0LmRpciwiXCJcbiIsc2VwPSIiKQpjbWQub3V0PC1wYXN0ZShjbWQub3V0LCJzb3VyY2UoXCIiLGRhdGFsb2FkLmZpbGVwYXRoLCJcIilcbiIsc2VwPSIiKQpjbWQub3V0PC1wYXN0ZShjbWQub3V0LCJFT0ZcbiIsc2VwPSIiKQpjYXQoY21kLm91dCxmaWxlPXNjcmlwdC5maWxlcGF0aCxhcHBlbmQ9RikKc3lzdGVtKHBhc3RlKCJjaG1vZCA3MDAgIixzY3JpcHQuZmlsZXBhdGgsc2VwPSIiKSkKI2NhdCgiYnN1YiA8ICIsc2NyaXB0LmZpbGVwYXRoLCJcbiIsc2VwPSIiLGZpbGU9am9ic3ViLmZpbGVwYXRoLGFwcGVuZD1UKQpjYXQoInNiYXRjaCAiLHNjcmlwdC5maWxlcGF0aCwiXG4iLHNlcD0iIixmaWxlPWpvYnN1Yi5maWxlcGF0aCxhcHBlbmQ9VCkKCn0Kc3lzdGVtKHBhc3RlKCJjaG1vZCA3MDAgIixqb2JzdWIuZmlsZXBhdGgsIlxuIixzZXA9IiIpKQoKCgoKCgoKCgoKIyA2LjMuMSBmb3IgZWFjaCBjdi50aHJlc2hvbGQsIGdlbmVyYXRlIHRoZSBoY2x1c3QgdHJlZSBmb3IgcmV2aWV3IGJ5IGV5ZXMgdG8gZGVjaWRlIHBvdGVudGlhbCBoZWlnaHRzIG9mIGN1dCBvbiB0aGUgdHJlZQpyLmZpbGVwYXRoPC0iL2hvbWUveWFueGl0aW5nL1Jlc2VhcmNoL0dSQURTX1NBUkMvUnByb2dyYW0vV0dDTkFfdW5hZGp1c3RlZF9BTExfZmlsdGVyaW5nLzZfMV9zYW1wbGV0cmVlLlIiCmRhdGEuZGlyPC0iL2hvbWUveWFueGl0aW5nL1Jlc2VhcmNoL0dSQURTX1NBUkMvUmVzdWx0c19zdW1tYXJ5X0JBTF9oZzM4L1dHQ05BX0JBTC91bmFkanVzdGVkX0FMTF9maWx0ZXJpbmciCiNjdi50aHJlc2hvbGQudmVjdDwtYygwLDAuMjU2NSwwLjMxNDgsMC4zODQ0LDAuNTA1OCwwLjY2NTEsMC44Mzc1LDEuMDY2NiwxLjUyMzMsMy40NzAyKQpjdi50aHJlc2hvbGQudmVjdDwtMApzY3JpcHQuZGlyPC1maWxlLnBhdGgoZGF0YS5kaXIsInNjcmlwdHNfc2FtcGxldHJlZV9vdXRsaWVyY29tcGFyZSIpCmlmKGZpbGUuZXhpc3RzKHNjcmlwdC5kaXIpPT1GKXsKZGlyLmNyZWF0ZShzY3JpcHQuZGlyKQp9Cgpqb2JzdWIuZmlsZXBhdGg8LWZpbGUucGF0aChzY3JpcHQuZGlyLCJqb2JzdWIuYmF0IikKaWYoZmlsZS5leGlzdHMoam9ic3ViLmZpbGVwYXRoKSl7CmZpbGUucmVtb3ZlKGpvYnN1Yi5maWxlcGF0aCkKfQpmaWxlLmNyZWF0ZShqb2JzdWIuZmlsZXBhdGgpCgpmb3IoaSBpbiAxOmxlbmd0aChjdi50aHJlc2hvbGQudmVjdCkpewoJCmN2LnRocmVzaG9sZDwtY3YudGhyZXNob2xkLnZlY3RbaV0Kc2NyaXB0LmZpbGVwYXRoPC1maWxlLnBhdGgoc2NyaXB0LmRpcixwYXN0ZSgiY3YiLGN2LnRocmVzaG9sZCwiLnNoIixzZXA9IiIpKQojY21kLm91dDwtcGFzdGUoIiNCU1VCIC1xIGthbWluc2tpIC1uIDEgLU0gODAwMCAtUiBcInNwYW5bcHRpbGU9MV0gcnVzYWdlW21lbT04MDAwXVwiIC1KICIscGFzdGUoImN2Iixjdi50aHJlc2hvbGQsc2VwPSIiKSwiIC1vICIsc2NyaXB0LmZpbGVwYXRoLCIubyVKIC1lICIsc2NyaXB0LmZpbGVwYXRoLCIuZSVKXG4iLHNlcD0iIikKI2NtZC5vdXQ8LXBhc3RlKCIjIS9iaW4vYmFzaCIsIlxuIiwiI1NCQVRDSCAtLXBhcnRpdGlvbj1waV9rYW1pbnNraSIsIlxuIiwiI1NCQVRDSCAtLWpvYi1uYW1lPSIscGFzdGUoImN2Iixjdi50aHJlc2hvbGQsc2VwPSIiKSwiXG4iLCIjU0JBVENIIC0tbnRhc2tzPTEgLS1ub2Rlcz0xIC0tY3B1cy1wZXItdGFzaz0xIiwiXG4iLCIjU0JBVENIIC0tbWVtPTE2MDAwIiwiXG4iLCIjU0JBVENIIC0tdGltZT0xNjg6MDA6MDAiLCJcbiIsIiNTQkFUQ0ggLS1tYWlsLXR5cGU9Tk9ORSIsIlxuIiwiI1NCQVRDSCAtLWVycm9yPSIsc2NyaXB0LmZpbGVwYXRoLCIuZSVKXG4iLCIjU0JBVENIIC0tb3V0cHV0PSIsc2NyaXB0LmZpbGVwYXRoLCIubyVKIiwiXG4iLHNlcD0iIikKI2NtZC5vdXQ8LXBhc3RlKGNtZC5vdXQsIi91c3IvYmluL1IgLS12YW5pbGxhPDxFT0ZcbiIsc2VwPSIiKQpjbWQub3V0PC0iL3Vzci9iaW4vUiAtLXZhbmlsbGE8PEVPRlxuIgpjbWQub3V0PC1wYXN0ZShjbWQub3V0LCJyZGF0YS5maWxlcGF0aDwtXCIiLGZpbGUucGF0aChkYXRhLmRpcixwYXN0ZSgiQ1YiLGN2LnRocmVzaG9sZCwiX2NvbXBhcmUiLHNlcD0iIiksImlucHV0X2RhdGEuUkRhdGEiKSwiXCJcbiIsc2VwPSIiKQpjbWQub3V0PC1wYXN0ZShjbWQub3V0LCJzb3VyY2UoXCIiLHIuZmlsZXBhdGgsIlwiKVxuIixzZXA9IiIpCmNtZC5vdXQ8LXBhc3RlKGNtZC5vdXQsIkVPRlxuIixzZXA9IiIpCmNhdChjbWQub3V0LGZpbGU9c2NyaXB0LmZpbGVwYXRoLGFwcGVuZD1GKQpzeXN0ZW0ocGFzdGUoImNobW9kIDcwMCAiLHNjcmlwdC5maWxlcGF0aCxzZXA9IiIpKQpjYXQoInNiYXRjaCAiLHNjcmlwdC5maWxlcGF0aCwiXG4iLHNlcD0iIixmaWxlPWpvYnN1Yi5maWxlcGF0aCxhcHBlbmQ9VCkKCn0Kc3lzdGVtKHBhc3RlKCJjaG1vZCA3MDAgIixqb2JzdWIuZmlsZXBhdGgsIlxuIixzZXA9IiIpKQoKCgoKCgoKCgoKCiMgNi4zLjIgcmV2aWV3IHRoZSB0cmVlIGZyb20gNi4zLjEgYW5kIGdlbmVyYXRlIGEgZmlsZSAoaGNsdXN0X2N1dF9oZWlnaHRzX2xpc3QudHh0KSBjb250YWluaW5nIHRoZSBwb3RlbnRpYWwgY3V0IGhlaWdodHMgZm9yIGVhY2ggZ2l2ZW4gY3YudGhyZXNob2xkCiMJCXRoZSBmaXJzdCBoZWlnaHQgaW4gaGNsdXN0X2N1dF9oZWlnaHRzX2xpc3QudHh0IGhhcyB0byBiZSBoaWdoZXIgdGhhbiB0aGUgYmlnZ2VzdCBoZWlnaHQgaW4gc2FtcGxldHJlZV9oZWlnaHRfbGlzdC50eHQKIwkJZm9yIGVhY2ggY3YudGhyZXNob2xkIGFuZCBlYWNoIGdpdmVuIGN1dCBoZWlnaHQsIGdlbmVyYXRlIHRoZSBzb2Z0IHBvd2VyIHBpY2tpbmcgZmlndXJlIHRvIHBpY2sgc29mdCBwb3dlcgpyLmZpbGVwYXRoPC0iL2hvbWUveWFueGl0aW5nL1Jlc2VhcmNoL0dSQURTX1NBUkMvUnByb2dyYW0vV0dDTkFfdW5hZGp1c3RlZF9BTExfZmlsdGVyaW5nLzZfMl9zZnRwaWNraW5nLlIiCmRhdGEuZGlyPC0iL2hvbWUveWFueGl0aW5nL1Jlc2VhcmNoL0dSQURTX1NBUkMvUmVzdWx0c19zdW1tYXJ5X0JBTF9oZzM4L1dHQ05BX0JBTC91bmFkanVzdGVkX0FMTF9maWx0ZXJpbmciCmN2LnRocmVzaG9sZC52ZWN0PC0wCgpzY3JpcHQuZGlyPC1maWxlLnBhdGgoZGF0YS5kaXIsInNjcmlwdHNfc2Z0cGlja2luZ19vdXRsaWVyY29tcGFyZSIpCmlmKGZpbGUuZXhpc3RzKHNjcmlwdC5kaXIpPT1GKXsKZGlyLmNyZWF0ZShzY3JpcHQuZGlyKQp9Cgpqb2JzdWIuZmlsZXBhdGg8LWZpbGUucGF0aChzY3JpcHQuZGlyLCJqb2JzdWIuYmF0IikKaWYoZmlsZS5leGlzdHMoam9ic3ViLmZpbGVwYXRoKSl7CmZpbGUucmVtb3ZlKGpvYnN1Yi5maWxlcGF0aCkKfQpmaWxlLmNyZWF0ZShqb2JzdWIuZmlsZXBhdGgpCgpmb3IoaSBpbiAxOmxlbmd0aChjdi50aHJlc2hvbGQudmVjdCkpewpjdi50aHJlc2hvbGQ8LWN2LnRocmVzaG9sZC52ZWN0W2ldCgpvdXRwdXQuZGlyPC1maWxlLnBhdGgoZGF0YS5kaXIscGFzdGUoIkNWIixjdi50aHJlc2hvbGQsIl9jb21wYXJlIixzZXA9IiIpKQpyZGF0YS5maWxlcGF0aDwtZmlsZS5wYXRoKG91dHB1dC5kaXIsImlucHV0X2RhdGEuUkRhdGEiKQpjdXRoZWlnaHQuZmlsZXBhdGg8LWZpbGUucGF0aChvdXRwdXQuZGlyLCJoY2x1c3RfY3V0X2hlaWdodHNfbGlzdC50eHQiKQoKc2NyaXB0LmZpbGVwYXRoPC1maWxlLnBhdGgoc2NyaXB0LmRpcixwYXN0ZSgiY3YiLGN2LnRocmVzaG9sZCwiLnNoIixzZXA9IiIpKQojY21kLm91dDwtcGFzdGUoIiNCU1VCIC1xIGthbWluc2tpIC1uIDEwIC1NIDY0MDAwIC1SIFwic3BhbltwdGlsZT0xMF0gcnVzYWdlW21lbT02NDAwMF1cIiAtSiAiLHBhc3RlKCJjdiIsY3YudGhyZXNob2xkLHNlcD0iIiksIiAtbyAiLHNjcmlwdC5maWxlcGF0aCwiLm8lSiAtZSAiLHNjcmlwdC5maWxlcGF0aCwiLmUlSlxuIixzZXA9IiIpCiNjbWQub3V0PC1wYXN0ZSgiIyEvYmluL2Jhc2giLCJcbiIsIiNTQkFUQ0ggLS1wYXJ0aXRpb249cGlfa2FtaW5za2kiLCJcbiIsIiNTQkFUQ0ggLS1qb2ItbmFtZT0iLHBhc3RlKCJjdiIsY3YudGhyZXNob2xkLHNlcD0iIiksIlxuIiwiI1NCQVRDSCAtLW50YXNrcz0xIC0tbm9kZXM9MSAtLWNwdXMtcGVyLXRhc2s9MTAiLCJcbiIsIiNTQkFUQ0ggLS1tZW09NjQwMDAiLCJcbiIsIiNTQkFUQ0ggLS10aW1lPTE2ODowMDowMCIsIlxuIiwiI1NCQVRDSCAtLW1haWwtdHlwZT1OT05FIiwiXG4iLCIjU0JBVENIIC0tZXJyb3I9IixzY3JpcHQuZmlsZXBhdGgsIi5lJUpcbiIsIiNTQkFUQ0ggLS1vdXRwdXQ9IixzY3JpcHQuZmlsZXBhdGgsIi5vJUoiLCJcbiIsc2VwPSIiKQojY21kLm91dDwtcGFzdGUoY21kLm91dCwiL3Vzci9iaW4vUiAtLXZhbmlsbGE8PEVPRlxuIixzZXA9IiIpCmNtZC5vdXQ8LSIvdXNyL2Jpbi9SIC0tdmFuaWxsYTw8RU9GXG4iCmNtZC5vdXQ8LXBhc3RlKGNtZC5vdXQsInJkYXRhLmZpbGVwYXRoPC1cIiIscmRhdGEuZmlsZXBhdGgsIlwiXG4iLHNlcD0iIikKY21kLm91dDwtcGFzdGUoY21kLm91dCwiY3V0aGVpZ2h0LmZpbGVwYXRoPC1cIiIsY3V0aGVpZ2h0LmZpbGVwYXRoLCJcIlxuIixzZXA9IiIpCmNtZC5vdXQ8LXBhc3RlKGNtZC5vdXQsIm91dHB1dC5kaXI8LVwiIixvdXRwdXQuZGlyLCJcIlxuIixzZXA9IiIpCmNtZC5vdXQ8LXBhc3RlKGNtZC5vdXQsInNvdXJjZShcIiIsci5maWxlcGF0aCwiXCIpXG4iLHNlcD0iIikKY21kLm91dDwtcGFzdGUoY21kLm91dCwiRU9GXG4iLHNlcD0iIikKY2F0KGNtZC5vdXQsZmlsZT1zY3JpcHQuZmlsZXBhdGgsYXBwZW5kPUYpCnN5c3RlbShwYXN0ZSgiY2htb2QgNzAwICIsc2NyaXB0LmZpbGVwYXRoLHNlcD0iIikpCmNhdCgic2JhdGNoICIsc2NyaXB0LmZpbGVwYXRoLCJcbiIsc2VwPSIiLGZpbGU9am9ic3ViLmZpbGVwYXRoLGFwcGVuZD1UKQoKfQpzeXN0ZW0ocGFzdGUoImNobW9kIDcwMCAiLGpvYnN1Yi5maWxlcGF0aCwiXG4iLHNlcD0iIikpCgoKIyA2LjMuMyByZXZpZXcgdGhlIHNvZnQgcG93ZXIgcGlja2luZyBmaWd1cmUgYW5kIHBpY2sgdGhlIHNvZnQgcG93ZXIgZm9yIGZpbmFsIGFuYWx5c2lzIHRvIHNhdmUgaXQgaW4gdGhlIGZpbGUgc2Z0X3Bvd2VyX2xpc3QudHh0CnIuZmlsZXBhdGg8LSIvaG9tZS95YW54aXRpbmcvUmVzZWFyY2gvR1JBRFNfU0FSQy9ScHJvZ3JhbS9XR0NOQV91bmFkanVzdGVkX0FMTF9maWx0ZXJpbmcvNl8zX2NsdXN0ZXJpbmcuUiIKZGF0YS5kaXI8LSIvaG9tZS95YW54aXRpbmcvUmVzZWFyY2gvR1JBRFNfU0FSQy9SZXN1bHRzX3N1bW1hcnlfQkFMX2hnMzgvV0dDTkFfQkFML3VuYWRqdXN0ZWRfQUxMX2ZpbHRlcmluZyIKI2N2LnRocmVzaG9sZC52ZWN0PC1jKDAsMSwxLjUsMiwyLjUsMywzLjUsNCkKY3YudGhyZXNob2xkLnZlY3Q8LTAKCnNjcmlwdC5kaXI8LWZpbGUucGF0aChkYXRhLmRpciwic2NyaXB0c19jbHVzdGVyaW5nX291dGxpZXJjb21wYXJlIikKaWYoZmlsZS5leGlzdHMoc2NyaXB0LmRpcik9PUYpewpkaXIuY3JlYXRlKHNjcmlwdC5kaXIpCn0KCmpvYnN1Yi5maWxlcGF0aDwtZmlsZS5wYXRoKHNjcmlwdC5kaXIsImpvYnN1Yi5iYXQiKQppZihmaWxlLmV4aXN0cyhqb2JzdWIuZmlsZXBhdGgpKXsKZmlsZS5yZW1vdmUoam9ic3ViLmZpbGVwYXRoKQp9CmZpbGUuY3JlYXRlKGpvYnN1Yi5maWxlcGF0aCkKCmZvcihpIGluIDE6bGVuZ3RoKGN2LnRocmVzaG9sZC52ZWN0KSl7CmN2LnRocmVzaG9sZDwtY3YudGhyZXNob2xkLnZlY3RbaV0KCm91dHB1dC5kaXI8LWZpbGUucGF0aChkYXRhLmRpcixwYXN0ZSgiQ1YiLGN2LnRocmVzaG9sZCwiX2NvbXBhcmUiLHNlcD0iIikpCnJkYXRhLmZpbGVwYXRoPC1maWxlLnBhdGgob3V0cHV0LmRpciwic2Z0X3BpY2tpbmcuUkRhdGEiKQpzZnRwb3dlci5maWxlcGF0aDwtZmlsZS5wYXRoKG91dHB1dC5kaXIsInNmdF9wb3dlcl9saXN0LnR4dCIpCgpzY3JpcHQuZmlsZXBhdGg8LWZpbGUucGF0aChzY3JpcHQuZGlyLHBhc3RlKCJjdiIsY3YudGhyZXNob2xkLCIuc2giLHNlcD0iIikpCiNjbWQub3V0PC1wYXN0ZSgiI0JTVUIgLXEga2FtaW5za2kgLW4gMTAgLU0gNjQwMDAgLVIgXCJzcGFuW3B0aWxlPTEwXSBydXNhZ2VbbWVtPTY0MDAwXVwiIC1KICIscGFzdGUoImN2Iixjdi50aHJlc2hvbGQsc2VwPSIiKSwiIC1vICIsc2NyaXB0LmZpbGVwYXRoLCIubyVKIC1lICIsc2NyaXB0LmZpbGVwYXRoLCIuZSVKXG4iLHNlcD0iIikKI2NtZC5vdXQ8LXBhc3RlKCIjIS9iaW4vYmFzaCIsIlxuIiwiI1NCQVRDSCAtLXBhcnRpdGlvbj1waV9rYW1pbnNraSIsIlxuIiwiI1NCQVRDSCAtLWpvYi1uYW1lPSIscGFzdGUoImN2Iixjdi50aHJlc2hvbGQsc2VwPSIiKSwiXG4iLCIjU0JBVENIIC0tbnRhc2tzPTEgLS1ub2Rlcz0xIC0tY3B1cy1wZXItdGFzaz0xMCIsIlxuIiwiI1NCQVRDSCAtLW1lbT02NDAwMCIsIlxuIiwiI1NCQVRDSCAtLXRpbWU9MTY4OjAwOjAwIiwiXG4iLCIjU0JBVENIIC0tbWFpbC10eXBlPU5PTkUiLCJcbiIsIiNTQkFUQ0ggLS1lcnJvcj0iLHNjcmlwdC5maWxlcGF0aCwiLmUlSlxuIiwiI1NCQVRDSCAtLW91dHB1dD0iLHNjcmlwdC5maWxlcGF0aCwiLm8lSiIsIlxuIixzZXA9IiIpCiNjbWQub3V0PC1wYXN0ZShjbWQub3V0LCJSIC0tdmFuaWxsYTw8RU9GXG4iLHNlcD0iIikKY21kLm91dDwtIi91c3IvYmluL1IgLS12YW5pbGxhPDxFT0ZcbiIKY21kLm91dDwtcGFzdGUoY21kLm91dCwicmRhdGEuZmlsZXBhdGg8LVwiIixyZGF0YS5maWxlcGF0aCwiXCJcbiIsc2VwPSIiKQpjbWQub3V0PC1wYXN0ZShjbWQub3V0LCJzZnRwb3dlci5maWxlcGF0aDwtXCIiLHNmdHBvd2VyLmZpbGVwYXRoLCJcIlxuIixzZXA9IiIpCmNtZC5vdXQ8LXBhc3RlKGNtZC5vdXQsIm91dHB1dC5kaXI8LVwiIixvdXRwdXQuZGlyLCJcIlxuIixzZXA9IiIpCmNtZC5vdXQ8LXBhc3RlKGNtZC5vdXQsInNvdXJjZShcIiIsci5maWxlcGF0aCwiXCIpXG4iLHNlcD0iIikKY21kLm91dDwtcGFzdGUoY21kLm91dCwiRU9GXG4iLHNlcD0iIikKY2F0KGNtZC5vdXQsZmlsZT1zY3JpcHQuZmlsZXBhdGgsYXBwZW5kPUYpCnN5c3RlbShwYXN0ZSgiY2htb2QgNzAwICIsc2NyaXB0LmZpbGVwYXRoLHNlcD0iIikpCmNhdCgic2JhdGNoICIsc2NyaXB0LmZpbGVwYXRoLCJcbiIsc2VwPSIiLGZpbGU9am9ic3ViLmZpbGVwYXRoLGFwcGVuZD1UKQoKfQpzeXN0ZW0ocGFzdGUoImNobW9kIDcwMCAiLGpvYnN1Yi5maWxlcGF0aCwiXG4iLHNlcD0iIikpCmBgYAoKVGhlIGhpZXJhcmhpY2FsIGNsdXN0ZXJpbmcgdHJlZSBvZiBhbGwgdGhlIHNhbXBsZXMgaXMgc2hvd24gaW4gYHIgZmlndXJlX251bXNfMShuYW1lPSJoY2x1c3RfdW5hZGp1c3RlZF8xIixkaXNwbGF5PSJjaXRlIilgCmBgYHtyIGZpZy5jYXA9ZmlndXJlX251bXNfMShuYW1lPSJoY2x1c3RfdW5hZGp1c3RlZF8xIixjYXB0aW9uPSJIaWVyYXJoaWNhbCBjbHVzdGVyaW5nIHRyZWUgb2YgYWxsIHRoZSBzYW1wbGVzIGZvciB0aGUgdW5hZGp1c3RlZCBkYXRhLiIpLGVjaG89VFJVRSxjYWNoZT1GQUxTRSxyZXN1bHRzPSdoaWRlJ30KI3dnY25hLmRpcjwtb3V0cHV0LmRpcgp3Z2NuYS5kaXI8LWZpbGUucGF0aChob21lLmRpciwic2NyYXRjaC9HUkFEUy9TQVJDX3Jlc3VsdHMvUmVzdWx0c19zdW1tYXJ5X1BCTUNfaGczOC9iYXNlbGluZS9XR0NOQS9XR0NOQV9ERVNlcTJOb3JtX29yaWdpbmFsIikKYGBgCmByIGZpZ3VyZV9udW1zXzEobmFtZT0iaGNsdXN0X3VuYWRqdXN0ZWRfMSIpYApgYGB7cn0Ka25pdHI6OmluY2x1ZGVfZ3JhcGhpY3MoZmlsZS5wYXRoKHdnY25hLmRpciwiaGNsdXN0X3RyZWVfYWxsc2FtcGxlcy5qcGciKSkKYGBgCldlIGlkZW50aWZpZWQgMTUgZGlmZmVyZW50IGN1dHRpbmcgaGVpZ2h0cyBvbiB0aGUgaGllcmFyY2hpY2FsIGNsdXN0ZXJpbmcgdHJlZSBpbmNsdWRpbmcgdGhlIGhlaWdodCB0aGF0IGRvZXMgbm90IHJlbW92ZSBhbnkgc2FtcGxlcy4gVGhlc2UgY2hvc2VuIGN1dHRpbmcgaGVpZ2h0cyBhbmQgdGhlaXIgY29ycmVzcG9uZGluZyBudW1iZXIgb2YgaWRlbnRpZmllZCBvdXRsaWVycyBhbmQgdGhlIGxpc3Qgb2Ygb3V0bGllcnMgYXJlIHNob3duIGluIGByIHRhYmxlX251bXNfMSgid2djbmFfY3V0aGVpZ2h0c190YWJsZV8xIixkaXNwbGF5PSJjaXRlIilgLiAKYGBge3J9CiNkYXRhLmZpbGVwYXRoPC0iL1VzZXJzL3lhbnhpdGluZy9NeVZvbHVtZXMvR1JBQ0Uvc2NyYXRjaC9HUkFEUy9TQVJDX3Jlc3VsdHMvUmVzdWx0c19zdW1tYXJ5X0JBTF9oZzM4L1dHQ05BX0JBTC9DVjBfY29tcGFyZS9jdXRoZWlnaHRfMl9zZnQudHh0IgpkYXRhLmZpbGVwYXRoPC1maWxlLnBhdGgod2djbmEuZGlyLCJjdXRoZWlnaHRfMl9zZnQudHh0IikKdGVtcC5tYXRyaXg8LXJlYWQudGFibGUoZGF0YS5maWxlcGF0aCxzZXA9Ilx0IixoZWFkZXI9VCxjaGVjay5uYW1lcz1GLHN0cmluZ3NBc0ZhY3RvcnM9RkFMU0UpCmZvcihpIGluIDE6bnJvdyh0ZW1wLm1hdHJpeCkpewogIGlmKGk9PTEpewogICAgbmV4dAogIH0KICB0ZW1wLm5hbWVzPC10ZW1wLm1hdHJpeFtpLTEsM10KCiAgaWYodGVtcC5uYW1lcyE9IiIpewogICAgdGVtcC5uYW1lczwtcGFzdGUodGVtcC5uYW1lcyx0ZW1wLm1hdHJpeFtpLDNdLHNlcD0iOyIpCiAgfWVsc2V7CiAgICB0ZW1wLm5hbWVzPC10ZW1wLm1hdHJpeFtpLDNdCiAgfQogIHRlbXAubWF0cml4W2ksM108LXRlbXAubmFtZXMKfQoKdGVtcC5tYXRyaXhbLDJdPC1jdW1zdW0odGVtcC5tYXRyaXhbLDJdKQoKa2FibGUodGVtcC5tYXRyaXgsZGlnaXRzID0gNCxyb3cubmFtZXM9RkFMU0UsY2FwdGlvbj10YWJsZV9udW1zXzEobmFtZT0id2djbmFfY3V0aGVpZ2h0c190YWJsZV8xIixjYXB0aW9uPSJUYWJsZSBvZiB0aGUgY2hvc2VuIGN1dHRpbmcgaGVpZ2h0cyBvbiB0aGUgaGllcmFyY2hpY2FsIGNsdXN0ZXJpbmcgdHJlZSBmb3IgdGhlIHVuYWRqdXN0ZWQgZGF0YSBhbmQgdGhlaXIgY29ycmVzcG9uZGluZyBudW1iZXIgb2Ygb3V0bGllcnMgYW5kIHRoZSBsaXN0IG9mIG91dGxpZXJzLiIpKSAlPiUga2FibGVfc3R5bGluZyhib290c3RyYXBfb3B0aW9ucyA9IGMoInN0cmlwZWQiLCAiaG92ZXIiLCAiY29uZGVuc2VkIiwicmVzcG9uc2l2ZSIpLCBmdWxsX3dpZHRoPUZBTFNFLHBvc2l0aW9uPSJsZWZ0IikgCiN3cml0ZS50YWJsZSh0ZW1wLm1hdHJpeCxzZXA9Ilx0Iixyb3cubmFtZXM9Rixjb2wubmFtZXM9VCxxdW90ZT1GKQpgYGAKClRoZSBXR0NOQSByZXN1bHRzIG9mIHRoZXNlIDcgY2hvc2VuIGN1dHRpbmcgaGVpZ2h0cyBhcmUgY29tcGFyZWQgYW5kIHNob3duIGluIGByIGZpZ3VyZV9udW1zXzEoIndnY25hX2NvbXBhcmVfdW5hZGp1c3RlZCIsZGlzcGxheT0iY2l0ZSIpYC4KCmBgYHtyIGZpZy5jYXA9ZmlndXJlX251bXNfMShuYW1lPSJ3Z2NuYV9jb21wYXJlX3VuYWRqdXN0ZWQiLGNhcHRpb249IkhlYXRtYXAgY29tcGFyaW5nIHRoZSBjbHVzdGVyaW5nIHJlc3VsdHMgb2YgdGhlIDcgY2hvc2VuIGN1dHRpbmcgaGVpZ2h0cyBmb3IgdGhlIHVuYWRqdXN0ZWQgZGF0YS4iKSxlY2hvPUZBTFNFLGNhY2hlPUZBTFNFLHJlc3VsdHM9J2hpZGUnfQpgYGAKYHIgZmlndXJlX251bXNfMShuYW1lPSJ3Z2NuYV9jb21wYXJlX3VuYWRqdXN0ZWQiKWAKYGBge3J9CmtuaXRyOjppbmNsdWRlX2dyYXBoaWNzKGZpbGUucGF0aCh3Z2NuYS5kaXIsImRlbmRyb2dyYW1fb3V0bGllcnNfY29tcGFyZS5qcGciKSkKYGBgCgpCYXNlZCBvbiB0aGUgY29tcGFyaXNvbiBpbiBgciBmaWd1cmVfbnVtc18xKCJ3Z2NuYV9jb21wYXJlX3VuYWRqdXN0ZWQiLGRpc3BsYXk9ImNpdGUiKWAsIHdlIGRlY2lkZWQgdG8gdXNlIHRoZSBjdXR0aW5nIGhlaWdodD0gd2hpY2ggZGVmaW5lZCAxNSBvdXRsaWVycyBmb3IgdGhlIFdHQ05BIGFuYWx5c2lzLiBUaGUgaWRlbnRpZmllZCBnZW5lIG1vZHVsZXMgYW5kIHRoZWlyIGNvcnJlbGF0aW9uIHdpdGggdGhlIGdpdmVuIGNsaW5pY2FsIHRyYWlzIGFyZSBzaG93biBpbiAuCgoKV2UgaGFkIHRvIHJlZHJhdyB0aGUgaGVhdG1hcCBvZiBjb3JyZWxhdGlvbiBiZXR3ZWVuIGNsaW5pY2FsIHRyYWl0cyBhbmQgbW9kdWxlcy4KYGBge3IgZmlnLndpZHRoPTExLGZpZy5oZWlnaHQ9MTZ9CiMtLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0KIyAvVXNlcnMveWFueGl0aW5nL015Vm9sdW1lcy9HUkFDRS9ScHJvZ3JhbS9HUkFEUy9CQUxfZmluYWxfaGczOF8yMDE4MDcyNi9XR0NOQV91bmFkanVzdGVkX0FMTF9maWx0ZXJpbmcvNl8zX2NsdXN0ZXJpbmcuUgpsaWJyYXJ5KFdHQ05BKQplbmFibGVXR0NOQVRocmVhZHMoblRocmVhZHMgPSAxNSkKbGlicmFyeShncGxvdHMpCgoKb3V0cHV0LmRpcjwtZmlsZS5wYXRoKGhvbWUuZGlyLCJzY3JhdGNoL0dSQURTL1NBUkNfcmVzdWx0cy9SZXN1bHRzX3N1bW1hcnlfUEJNQ19oZzM4L2Jhc2VsaW5lL1dHQ05BL1dHQ05BX0RFU2VxMk5vcm1fb3JpZ2luYWwiKQpyZGF0YS5maWxlcGF0aDwtZmlsZS5wYXRoKG91dHB1dC5kaXIsIndnY25hX2N1dGhlaWdodHMuUkRhdGEiKQpzZnRwb3dlci5maWxlcGF0aDwtZmlsZS5wYXRoKG91dHB1dC5kaXIsInNmdF9wb3dlcl9saXN0LnR4dCIpCmxvYWQocmRhdGEuZmlsZXBhdGgpCgpteS5jb3IuZGlzdDwtZnVuY3Rpb24oeCxtZXRob2QubmFtZT0ic3BlYXJtYW4iKXsKIyBjYWxjdWxhdGUgdGhlIGNvcnJlbGF0aW9uIGJldHdlZW4gcm93cyBpbiB4IGFuZCByZXR1cm4gdGhlIGNvcnJlbGF0aW9uIG1hdHJpeCBhcyBhIGRpc3RhbmNlIG1hdHJpeAp5PC1jb3IodCh4KSxtZXRob2Q9bWV0aG9kLm5hbWUpCnlbaXMubmEoeSldPC0gLTEuNQpyZXR1cm4oYXMuZGlzdCgxLXkpKQp9CgoKIyBmb3IgZWFjaCBlbGVtZW50IGluIG91dGxpZXIubGlzdCwgcGljayBwb3dlciBhbmQgcHV0IHRoZW0gaW4gcG93ZXIudmVjdCBiZWxvdyBmb3IgZnVydGhlciBhbmFseXNpcwpwb3dlci52ZWN0PC1zY2FuKHNmdHBvd2VyLmZpbGVwYXRoKQoKaTwtMgoKZ2MoKQpjYXQoImk9IixpLCJcbiIsc2VwPSIiKQpteS5kYXRFeHByID0gZGF0RXhwcltzZXRkaWZmKHJvd25hbWVzKGRhdEV4cHIpLHVubGlzdChvdXRsaWVyLmxpc3RbMTppXSkpLCBdCgojcmVuYW1lIHRoZSBjb2x1bW5zIChyZW1vdmUgYWxsIGFmdGVyIF8gaW4gc2FtcGxlIG5hbWUpClNhcmNhbGVTYW1wbGVzID0gcm93bmFtZXMobXkuZGF0RXhwcikKc2FtcGxlUm93cyA9bWF0Y2goU2FyY2FsZVNhbXBsZXMscm93bmFtZXMoc2FtcGxlLm1hdHJpeCkpOwpzYW1wbGUubWF0cml4Lm5ldz1zYW1wbGUubWF0cml4W3NhbXBsZVJvd3MsXQp0cmFpdFJvd3MgPSBtYXRjaChzYW1wbGUubWF0cml4Lm5ld1ssMV0sY29sbmFtZXMoZGF0VHJhaXRzKSk7Cm15LmRhdFRyYWl0cyA9IGRhdFRyYWl0c1ssdHJhaXRSb3dzXTsKCm5HZW5lcyA9IG5jb2wobXkuZGF0RXhwcikKblNhbXBsZXMgPSBucm93KG15LmRhdEV4cHIpCgojIHNlbGVjdCB0aGUgcG93ZXIgYmFzZWQgb24gdGhlIHBsb3QgYWJvdmUKcG93ZXIuc2VsZWN0ZWQ8LXBvd2VyLnZlY3RbaV0KbmV0ID0gbmV0Lmxpc3RbW2ldXQoKIy0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0KIyBjYWxjdWxhdGUgdGhlIGNvcnJlbHRpb24gYmV0d2VlbiB0aGUgZWlnZW4gZ2VuZXMgb2YgdGhlIG1vZHVsZXMgdG8gc2VlIGlmIHdlIG5lZWQgdG8gY2hhbmdlIHRoZSBtZXJnZUN1dEhlaWdodApNRUxpc3QudGVzdDwtbW9kdWxlRWlnZW5nZW5lcyhteS5kYXRFeHByLGNvbG9ycz1tZXJnZWRDb2xvcnMpCk1Fcy50ZXN0PC1NRUxpc3QudGVzdCRlaWdlbmdlbmVzCk1FRGlzcy50ZXN0PC0xLWNvcihNRXMudGVzdCkKTUVUcmVlLnRlc3Q9aGNsdXN0KGFzLmRpc3QoTUVEaXNzLnRlc3QpLG1ldGhvZD0iYXZlcmFnZSIpCgojLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0KIyBwbG90IHRoZSBjb3JyZWxhdGlvbiBiZXR3ZWVuIHRoZSBtb2R1bGVzIHdpdGggdGhlIGNsaW5pY2FsIHRyYWl0cwpuR2VuZXMgPSBuY29sKG15LmRhdEV4cHIpOwpuU2FtcGxlcyA9IG5yb3cobXkuZGF0RXhwcik7CiMgUmVjYWxjdWxhdGUgTUVzIHdpdGggY29sb3IgbGFiZWxzCk1FczAgPSBtb2R1bGVFaWdlbmdlbmVzKG15LmRhdEV4cHIsIGxhYmVsczJjb2xvcnMobmV0JGNvbG9ycykpJGVpZ2VuZ2VuZXMKTUVzID0gb3JkZXJNRXMoTUVzMCkKY3AgPSBiaWNvckFuZFB2YWx1ZShNRXMsIHQobXkuZGF0VHJhaXRzKSkKbW9kdWxlVHJhaXRDb3IgPSBjcCRiaWNvcjsKbW9kdWxlVHJhaXRQdmFsdWUgPSBjcCRwOwoKb3V0cHV0LmZpbGVwYXRoPC1maWxlLnBhdGgob3V0cHV0LmRpcixwYXN0ZSgiaGVhdG1hcF90cmFpdF9jb3JyZWxhdGlvbl9tb2R1bGVzX291dGxpZXJzXyIsaSwiX3B2YWx1ZXNpZ19hZGpzdXRlZC5lcHMiLHNlcD0iIikpCiNwZGYoZmlsZSA9IG91dHB1dC5maWxlcGF0aCx3aWR0aCA9IDE1LGhlaWdodCA9IDkscGFwZXI9InNwZWNpYWwiKQojc2l6ZUdyV2luZG93KDE1LDkpCnBvc3RzY3JpcHQob3V0cHV0LmZpbGVwYXRoLHdpZHRoPTE0LGhlaWdodD0xNixwYXBlcj0ic3BlY2lhbCIpCiNwZGYoZmlsZT0iUGxvdHMvbW9kdWxlVHJhaXRSZWxhdGlvbnNoaXBzLnBkZiIsIHdpZHRoPTEwLCBoZWlnaHQ9Nik7CiMgV2lsbCBkaXNwbGF5IGNvcnJlbGF0aW9ucyBhbmQgdGhlaXIgcC12YWx1ZXMKdGV4dE1hdHJpeCA9IHBhc3RlKHNpZ25pZihtb2R1bGVUcmFpdENvciwgMiksICJcbigiLHNpZ25pZihtb2R1bGVUcmFpdFB2YWx1ZSwgMSksICIpIiwgc2VwID0gIiIpOwpkaW0odGV4dE1hdHJpeCkgPSBkaW0obW9kdWxlVHJhaXRDb3IpCnBhcihtYXIgPSBjKDEyLCAxMiwgMywgMSkpOwojIERpc3BsYXkgdGhlIGNvcnJlbGF0aW9uIHZhbHVlcyB3aXRoaW4gYSBoZWF0bWFwIHBsb3QKdGVtcC5tb2R1bGVUcmFpdFB2YWx1ZTwtbW9kdWxlVHJhaXRQdmFsdWUKdGVtcC5tb2R1bGVUcmFpdENvcjwtbW9kdWxlVHJhaXRDb3IKdGVtcC5tb2R1bGVUcmFpdENvclt0ZW1wLm1vZHVsZVRyYWl0UHZhbHVlPjAuMDVdPC1OQQoKbGFiZWxlZEhlYXRtYXAoTWF0cml4ID0gdGVtcC5tb2R1bGVUcmFpdENvciwKeExhYmVscyA9IHJvd25hbWVzKG15LmRhdFRyYWl0c1tdKSwKeUxhYmVscyA9IG5hbWVzKE1FcyksCnlTeW1ib2xzID0gbmFtZXMoTUVzKSwKY29sb3JMYWJlbHMgPSBGQUxTRSwKY29sb3JzID0gYmx1ZVdoaXRlUmVkKDUwKSwKdGV4dE1hdHJpeCA9IHRleHRNYXRyaXgsCiNjZXgudGV4dD0wLjgsCnNldFN0ZE1hcmdpbnMgPSBGQUxTRSwKY2V4LnRleHQgPSAwLjUsCnpsaW0gPSBjKC0xLDEpLAptYWluID0gcGFzdGUoIk1vZHVsZS10cmFpdCByZWxhdGlvbnNoaXBzIiksCm5hQ29sb3I9IndoaXRlIikKI3NhdmVQbG90KG91dHB1dC5maWxlcGF0aCx0eXBlPSJqcGVnIikKZGV2Lm9mZigpCgpgYGAKCgoKYGBge3IgZmlnLmNhcD1maWd1cmVfbnVtc18xKG5hbWU9IndnY25hX3VuYWRqdXN0ZWRfaGVhdG1hcF8xIixjYXB0aW9uPSJIZWF0bWFwIHNob3dpbmcgdGhlIGNvcnJlbGF0aW9uIGJldHdlZW4gdGhlIGlkZW50aWZpZWQgZ2VuZSBtb2R1bGVzIGFuZCB0aGUgY2xpbmljYWwgdHJhaXRzIGZvciB0aGUgdW5hZGp1c3RlZCBkYXRhLiIpLGVjaG89RkFMU0UsY2FjaGU9RkFMU0UscmVzdWx0cz0naGlkZSd9CmBgYApgciBmaWd1cmVfbnVtc18xKG5hbWU9IndnY25hX3VuYWRqdXN0ZWRfaGVhdG1hcF8xIilgCmBgYHtyfQprbml0cjo6aW5jbHVkZV9ncmFwaGljcyhmaWxlLnBhdGgod2djbmEuZGlyLCJoZWF0bWFwX3RyYWl0X2NvcnJlbGF0aW9uX21vZHVsZXNfb3V0bGllcnNfMl9wdmFsdWVzaWdfYWRqc3V0ZWQuanBnIikpCmBgYAoKYGBge3IgcmVzdWx0cz0naGlkZSd9Cm5ldDwtbmV0Lmxpc3RbWzJdXQpgYGAKClRoZSBXR0NOQSBhbmFseXNpcyBpZGVudGlmaWVkIGByIGxlbmd0aCh1bmlxdWUobmV0JGNvbG9ycykpYCBnZW5lIG1vZHVsZXMgYW5kIHRoZSBzaXplIG9mIHRoZXNlIG1vZHVsZXMgcmFuZ2VzIGZyb20gYHIgbWluKHRhYmxlKG5ldCRjb2xvcnMpKWAgdG8gYHIgbWF4KHRhYmxlKG5ldCRjb2xvcnMpKWAuIFRoZSByZXN1bHRzIGluIGByIGZpZ3VyZV9udW1zXzEoIndnY25hX3VuYWRqdXN0ZWRfaGVhdG1hcF8xIixkaXNwbGF5PSJjaXRlIilgIHByb3ZpZGVkIHRoZSBmb2xsb3dpbmcgb2JzZXJ2YXRpb25zOgoKMS4gQWdlIGlzIG9ubHkgY29ycmVsYXRlZCB3aXRoIG9uZSBnZW5lIG1vZHVsZSAoTUV3aGl0ZSkuIFRoaXMgbW9kdWxlIGlzIGFsc28gY29ycmVsYXRlZCB3aXRoLgpgYGB7ciBmaWcud2lkdGg9MTIsZmlnLmhlaWdodD0zfQojIG9ubHkgZGVtb25zdHJhdGUgdGhlIGdlbmUgbW9kdWxlcyB0aGF0IGNvcnJlbGF0ZSB3aXRoIGFnZQptb2R1bGVzLmNob3Nlbjwtcm93bmFtZXMobW9kdWxlVHJhaXRQdmFsdWUpW21vZHVsZVRyYWl0UHZhbHVlWywiQUdFIl08MC4wNV0KbXkubW9kdWxlVHJhaXRDb3I8LW1vZHVsZVRyYWl0Q29yW21vZHVsZXMuY2hvc2VuLF0KbXkubW9kdWxlVHJhaXRQdmFsdWU8LW1vZHVsZVRyYWl0UHZhbHVlW21vZHVsZXMuY2hvc2VuLF0KCnRleHRNYXRyaXggPSBwYXN0ZShzaWduaWYobXkubW9kdWxlVHJhaXRDb3IsIDIpLCAiXG4oIixzaWduaWYobXkubW9kdWxlVHJhaXRQdmFsdWUsIDEpLCAiKSIsIHNlcCA9ICIiKTsKZGltKHRleHRNYXRyaXgpID0gZGltKG15Lm1vZHVsZVRyYWl0Q29yKQpwYXIobWFyID0gYygxMiwgMTIsIDMsIDEpKTsKIyBEaXNwbGF5IHRoZSBjb3JyZWxhdGlvbiB2YWx1ZXMgd2l0aGluIGEgaGVhdG1hcCBwbG90CnRlbXAubW9kdWxlVHJhaXRQdmFsdWU8LW15Lm1vZHVsZVRyYWl0UHZhbHVlCnRlbXAubW9kdWxlVHJhaXRDb3I8LW15Lm1vZHVsZVRyYWl0Q29yCnRlbXAubW9kdWxlVHJhaXRDb3JbdGVtcC5tb2R1bGVUcmFpdFB2YWx1ZT4wLjA1XTwtTkEKCmxhYmVsZWRIZWF0bWFwKE1hdHJpeCA9IHQoYXMubWF0cml4KHRlbXAubW9kdWxlVHJhaXRDb3IpKSwKeExhYmVscyA9IHJvd25hbWVzKG15LmRhdFRyYWl0c1tdKSwKeUxhYmVscyA9IG1vZHVsZXMuY2hvc2VuLAp5U3ltYm9scyA9IG1vZHVsZXMuY2hvc2VuLApjb2xvckxhYmVscyA9IEZBTFNFLApjb2xvcnMgPSBibHVlV2hpdGVSZWQoNTApLAp0ZXh0TWF0cml4ID0gdGV4dE1hdHJpeCwKI2NleC50ZXh0PTAuOCwKc2V0U3RkTWFyZ2lucyA9IEZBTFNFLApjZXgudGV4dCA9IDAuNSwKemxpbSA9IGMoLTEsMSksCm1haW4gPSBwYXN0ZSgiTW9kdWxlLXRyYWl0IHJlbGF0aW9uc2hpcHMgKEFHRSkiKSwKbmFDb2xvcj0id2hpdGUiKQojc2F2ZVBsb3Qob3V0cHV0LmZpbGVwYXRoLHR5cGU9ImpwZWciKQpgYGAKCjIuIEdlbmRlciBpcyBjb3JyZWxhdGVkIHdpdGggYSBmZXcgZ2VuZSBtb2R1bGVzLCB3aGljaCBhcmUgYWxzbyBjb3JyZWxhdGVkIHdpdGggQkRGVkMsIEJERkVWMSBpbiBsaXRlcnMsIHN1Z2dlc3RpbmcgdGhlIGdlbmRlciBlZmZlY3Qgb24gdGhlIHVuYWRqdXN0ZWQgUEZUcyBhZ2Fpbi4KYGBge3IgZmlnLndpZHRoPTEyLGZpZy5oZWlnaHQ9OH0KIyBvbmx5IGRlbW9uc3RyYXRlIHRoZSBnZW5lIG1vZHVsZXMgdGhhdCBjb3JyZWxhdGUgd2l0aCBhZ2UKbW9kdWxlcy5jaG9zZW48LXJvd25hbWVzKG1vZHVsZVRyYWl0UHZhbHVlKVttb2R1bGVUcmFpdFB2YWx1ZVssIkdFTkRFUiJdPDAuMDVdCm15Lm1vZHVsZVRyYWl0Q29yPC1tb2R1bGVUcmFpdENvclttb2R1bGVzLmNob3NlbixdCm15Lm1vZHVsZVRyYWl0UHZhbHVlPC1tb2R1bGVUcmFpdFB2YWx1ZVttb2R1bGVzLmNob3NlbixdCgp0ZXh0TWF0cml4ID0gcGFzdGUoc2lnbmlmKG15Lm1vZHVsZVRyYWl0Q29yLCAyKSwgIlxuKCIsc2lnbmlmKG15Lm1vZHVsZVRyYWl0UHZhbHVlLCAxKSwgIikiLCBzZXAgPSAiIik7CmRpbSh0ZXh0TWF0cml4KSA9IGRpbShteS5tb2R1bGVUcmFpdENvcikKcGFyKG1hciA9IGMoMTIsIDEyLCAzLCAxKSk7CiMgRGlzcGxheSB0aGUgY29ycmVsYXRpb24gdmFsdWVzIHdpdGhpbiBhIGhlYXRtYXAgcGxvdAp0ZW1wLm1vZHVsZVRyYWl0UHZhbHVlPC1teS5tb2R1bGVUcmFpdFB2YWx1ZQp0ZW1wLm1vZHVsZVRyYWl0Q29yPC1teS5tb2R1bGVUcmFpdENvcgp0ZW1wLm1vZHVsZVRyYWl0Q29yW3RlbXAubW9kdWxlVHJhaXRQdmFsdWU+MC4wNV08LU5BCgpsYWJlbGVkSGVhdG1hcChNYXRyaXggPSB0ZW1wLm1vZHVsZVRyYWl0Q29yLAp4TGFiZWxzID0gcm93bmFtZXMobXkuZGF0VHJhaXRzW10pLAp5TGFiZWxzID0gbW9kdWxlcy5jaG9zZW4sCnlTeW1ib2xzID0gbW9kdWxlcy5jaG9zZW4sCmNvbG9yTGFiZWxzID0gRkFMU0UsCmNvbG9ycyA9IGJsdWVXaGl0ZVJlZCg1MCksCnRleHRNYXRyaXggPSB0ZXh0TWF0cml4LAojY2V4LnRleHQ9MC44LApzZXRTdGRNYXJnaW5zID0gRkFMU0UsCmNleC50ZXh0ID0gMC41LAp6bGltID0gYygtMSwxKSwKbWFpbiA9IHBhc3RlKCJNb2R1bGUtdHJhaXQgcmVsYXRpb25zaGlwcyAoR0VOREVSKSIpLApuYUNvbG9yPSJ3aGl0ZSIpCiNzYXZlUGxvdChvdXRwdXQuZmlsZXBhdGgsdHlwZT0ianBlZyIpCmBgYAoKMy4gUkFDRSBpcyBhbHNvIGNvcnJlbGF0ZWQgd2l0aCBhIGZldyBnZW5lIG1vZHVsZXMsIHdoaWNoIGFyZSBhbHNvIGNvcnJlbGF0ZWQgd2l0aCBQRlRzIGluIGxpdGVycy4KYGBge3IgZmlnLndpZHRoPTEyLGZpZy5oZWlnaHQ9Nn0KIyBvbmx5IGRlbW9uc3RyYXRlIHRoZSBnZW5lIG1vZHVsZXMgdGhhdCBjb3JyZWxhdGUgd2l0aCBhZ2UKbW9kdWxlcy5jaG9zZW48LXJvd25hbWVzKG1vZHVsZVRyYWl0UHZhbHVlKVttb2R1bGVUcmFpdFB2YWx1ZVssIlJBQ0UiXTwwLjA1XQpteS5tb2R1bGVUcmFpdENvcjwtbW9kdWxlVHJhaXRDb3JbbW9kdWxlcy5jaG9zZW4sXQpteS5tb2R1bGVUcmFpdFB2YWx1ZTwtbW9kdWxlVHJhaXRQdmFsdWVbbW9kdWxlcy5jaG9zZW4sXQoKdGV4dE1hdHJpeCA9IHBhc3RlKHNpZ25pZihteS5tb2R1bGVUcmFpdENvciwgMiksICJcbigiLHNpZ25pZihteS5tb2R1bGVUcmFpdFB2YWx1ZSwgMSksICIpIiwgc2VwID0gIiIpOwpkaW0odGV4dE1hdHJpeCkgPSBkaW0obXkubW9kdWxlVHJhaXRDb3IpCnBhcihtYXIgPSBjKDEyLCAxMiwgMywgMSkpOwojIERpc3BsYXkgdGhlIGNvcnJlbGF0aW9uIHZhbHVlcyB3aXRoaW4gYSBoZWF0bWFwIHBsb3QKdGVtcC5tb2R1bGVUcmFpdFB2YWx1ZTwtbXkubW9kdWxlVHJhaXRQdmFsdWUKdGVtcC5tb2R1bGVUcmFpdENvcjwtbXkubW9kdWxlVHJhaXRDb3IKdGVtcC5tb2R1bGVUcmFpdENvclt0ZW1wLm1vZHVsZVRyYWl0UHZhbHVlPjAuMDVdPC1OQQoKbGFiZWxlZEhlYXRtYXAoTWF0cml4ID0gdGVtcC5tb2R1bGVUcmFpdENvciwKeExhYmVscyA9IHJvd25hbWVzKG15LmRhdFRyYWl0c1tdKSwKeUxhYmVscyA9IG1vZHVsZXMuY2hvc2VuLAp5U3ltYm9scyA9IG1vZHVsZXMuY2hvc2VuLApjb2xvckxhYmVscyA9IEZBTFNFLApjb2xvcnMgPSBibHVlV2hpdGVSZWQoNTApLAp0ZXh0TWF0cml4ID0gdGV4dE1hdHJpeCwKI2NleC50ZXh0PTAuOCwKc2V0U3RkTWFyZ2lucyA9IEZBTFNFLApjZXgudGV4dCA9IDAuNSwKemxpbSA9IGMoLTEsMSksCm1haW4gPSBwYXN0ZSgiTW9kdWxlLXRyYWl0IHJlbGF0aW9uc2hpcHMgKFJBQ0UpIiksCm5hQ29sb3I9IndoaXRlIikKI3NhdmVQbG90KG91dHB1dC5maWxlcGF0aCx0eXBlPSJqcGVnIikKYGBgCjQuIFRoZXJlIGFyZSBzb21lIGdlbmUgbW9kdWxlcyB0aGF0IGFyZSBjb3JyZWxhdGVkIHdpdGggbWFueSBQRlRzLiBTb21lIG9mIHRoZW0gYXJlIGFsc28gY29ycmVsYXRlZCB3aXRoIEdFTkRFUiwgUkFDRS4gQnV0IHNvbWUgb2YgdGhlbSBhcmUgYWxzbyBjb3JyZWxhdGVkIHdpdGggUEZUcyBpbmRlcGVuZGVudGx5IG9mIEdFTkRFUiBhbmQgUkFDRS4KCmBgYHtyIGZpZy53aWR0aD0xMixmaWcuaGVpZ2h0PTExfQojIG9ubHkgZGVtb25zdHJhdGUgdGhlIGdlbmUgbW9kdWxlcyB0aGF0IGNvcnJlbGF0ZSB3aXRoIGFnZQp0ZW1wLm1hdHJpeDwtbW9kdWxlVHJhaXRQdmFsdWVbLGMoIkJERlZDIiwiRlZDUFJFRCIsInBwZnZjcHJlTmhhbmVzIiwicHBmdmNwb3N0TmhhbmVzIiwicHBmdmNwcmVHbGkiLCJwcGZ2Y3Bvc3RHbGkiLCJCREZFVjEiLCJGRVYxUFJFRCIsInBwZmV2cHJlTmhhbmVzIiwicHBmZXZwb3N0TmhhbmVzIiwicHBmZXZwcmVHbGkiLCJwcGZldnBvc3RHbGkiLCJCRERMQ08iLCJQUkVERExDTyIpXQp0ZW1wLm1hdHJpeDwtYXBwbHkodGVtcC5tYXRyaXg8MC4wNSwxLHN1bSkKbW9kdWxlcy5jaG9zZW48LXJvd25hbWVzKG1vZHVsZVRyYWl0UHZhbHVlKVt0ZW1wLm1hdHJpeD49MV0KbXkubW9kdWxlVHJhaXRDb3I8LW1vZHVsZVRyYWl0Q29yW21vZHVsZXMuY2hvc2VuLF0KbXkubW9kdWxlVHJhaXRQdmFsdWU8LW1vZHVsZVRyYWl0UHZhbHVlW21vZHVsZXMuY2hvc2VuLF0KCnRleHRNYXRyaXggPSBwYXN0ZShzaWduaWYobXkubW9kdWxlVHJhaXRDb3IsIDIpLCAiXG4oIixzaWduaWYobXkubW9kdWxlVHJhaXRQdmFsdWUsIDEpLCAiKSIsIHNlcCA9ICIiKTsKZGltKHRleHRNYXRyaXgpID0gZGltKG15Lm1vZHVsZVRyYWl0Q29yKQpwYXIobWFyID0gYygxMiwgMTIsIDMsIDEpKTsKIyBEaXNwbGF5IHRoZSBjb3JyZWxhdGlvbiB2YWx1ZXMgd2l0aGluIGEgaGVhdG1hcCBwbG90CnRlbXAubW9kdWxlVHJhaXRQdmFsdWU8LW15Lm1vZHVsZVRyYWl0UHZhbHVlCnRlbXAubW9kdWxlVHJhaXRDb3I8LW15Lm1vZHVsZVRyYWl0Q29yCnRlbXAubW9kdWxlVHJhaXRDb3JbdGVtcC5tb2R1bGVUcmFpdFB2YWx1ZT4wLjA1XTwtTkEKCmxhYmVsZWRIZWF0bWFwKE1hdHJpeCA9IHRlbXAubW9kdWxlVHJhaXRDb3IsCnhMYWJlbHMgPSByb3duYW1lcyhteS5kYXRUcmFpdHNbXSksCnlMYWJlbHMgPSBtb2R1bGVzLmNob3NlbiwKeVN5bWJvbHMgPSBtb2R1bGVzLmNob3NlbiwKY29sb3JMYWJlbHMgPSBGQUxTRSwKY29sb3JzID0gYmx1ZVdoaXRlUmVkKDUwKSwKdGV4dE1hdHJpeCA9IHRleHRNYXRyaXgsCiNjZXgudGV4dD0wLjgsCnNldFN0ZE1hcmdpbnMgPSBGQUxTRSwKY2V4LnRleHQgPSAwLjUsCnpsaW0gPSBjKC0xLDEpLAptYWluID0gcGFzdGUoIk1vZHVsZS10cmFpdCByZWxhdGlvbnNoaXBzIChQRlRzKSIpLApuYUNvbG9yPSJ3aGl0ZSIpCiNzYXZlUGxvdChvdXRwdXQuZmlsZXBhdGgsdHlwZT0ianBlZyIpCmBgYAoKCg==